Search code examples
c++templatescompiler-errors

Compiling C++ non-type template parameters outside headers?


This may be a silly question, but I have been struggling with this problem, and ChatGPT solutions aren't able to solve it.

I have the following class definition:

template<typename U, typename = std::enable_if_t<std::is_base_of_v<int, U>>>
class TestClass {
  public:
    auto print() -> void;
};

If I implement print() within the declaration / header itself, this is trivial:

template<typename U, std::enable_if_t<std::is_base_of_v<U, int>, int> = 0>
class TestClass
{
    auto print()
    {
       std::cout << "Hello";
    }
};

However, if I try to move this into a cpp file for it's implementation, I cannot get it to compile. Here is one of my attempts:

template<typename U, typename>
void TestClass<U, std::enable_if_t<std::is_base_of_v<int, U>>>::print()
{
    std::cout << "Printing from TestClass" << std::endl;
}

And here is that attempt's compilation error:

main.cpp:14:19: error: template argument for non-type template parameter must be an expression
void TestClass<U, std::enable_if_t<std::is_base_of_v<int, U>>>::print()
                  ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
main.cpp:6:71: note: template parameter is declared here
template<typename U, std::enable_if_t<std::is_base_of_v<U, int>, int> = 0>
                                                                      ^
1 error generated.

What is the correct syntax when moving this into a cpp file?


Solution

  • The common way of doing this(syntax-wise) is to specify the default argument in the first occurrence of the definition/declaration and ommit it in the next as shown below.

    This means that there are two ways to solve this depending upon which of the two code snippets given in your question you're using.

    Method 1

    //specify default argument here
    template<typename U, typename = std::enable_if_t<std::is_base_of_v<int, U>>>
    class TestClass {
      public:
        auto print() -> void;
    };
    //no default argument here
    template<typename U, typename N>
    void TestClass<U, N>::print() 
    {
           std::cout << "Hello"; 
    }
    

    working demo

    Method 2

    //specify the default argument here
    template<typename U, std::enable_if_t<std::is_base_of_v<U, int>, int> = 0>
    class TestClass
    {
        auto print() -> void;
        
    };
    //no need to specify default argument here
    template<typename U, std::enable_if_t<std::is_base_of_v<U, int>, int> N>
    void TestClass<U, N>::print()
    {
           std::cout << "Hello";
    }
    

    Working demo