Empty angle brackets in C++

197 Views Asked by At

When exploring RxCpp library I encountered the following sample which I cannot interpret.

    auto ints = rxcpp::observable<>::create(
        [](rxcpp::subscriber<int> s){
            s.on_next(1);
            s.on_next(2);
            s.on_completed();
    });

There are two declarations of observable class in the library:

template<class T, class SourceOperator>
class observable
    : public observable_base<T>
{
// ...
};

template<>
class observable<void, void>
{
// ...
};

What I was not able to comprehend is how the compiler manages to accept rxcpp::observable<>. piece. There could have been many explicit specializations of observable for different types, other than void,void.

The question is how the compiler interprets empty angle brackets in this code: rxcpp::observable<>.

I see no default template parameters in observable class, neither variadic template parameters which could explain this.

Then I thought it was somehow related to explicit template specialization, and tried to reproduce it in an isolated program, for instance like this

namespace isolated {
  template<class T>
  class Test {
  public:
    static void say() {
      cout << "I am generic" << endl;
    }
  };

  template<>
  class Test<int> {
  public:
    static void say() {
      cout << "I am integer" << endl;
    }
  };
}

int main() {
  isolated::Test<>::say(); // ERROR: too few arguments for class template.....
}

However it does not compile even though there is only one explicit specialization.

1

There are 1 best solutions below

2
NathanOliver On BEST ANSWER

What you are missing is

template<
     class T = void,
     class SourceObservable = typename std::conditional<std::is_same<T, void>::value,
         void, dynamic_observable<T>>::type>
 class observable;

from lines 142-146 of rx-predef.hpp

This forward declaration supplies default template arguments for the observable class and allows you to write observable<> which will use those default values. In your example that would be accomplished by adding

template<class T = int>
class Test;

Which gives you

namespace isolated {
  template<class T = int>
  class Test;

  template<class T>
  class Test {
  public:
    static void say() {
      cout << "I am generic" << endl;
    }
  };

  template<>
  class Test<int> {
  public:
    static void say() {
      cout << "I am integer" << endl;
    }
  };
}

int main() {
  isolated::Test<>::say(); // ERROR: too few arguments for class template.....
}

and outputs

I am integer

in this live example