Search code examples
c++templatesg++friend-function

linker error with templated friend function of templated class when using template independent enable_if


I'm dealing with a templated class with a templated friend function

template<typename T>
struct X {
  template<typename someX>
  auto friend f (someX x) -> std::enable_if_t<std::is_same_v<decltype(x.hidden), int>, int>;

private:
  T hidden = 42;
};

template<typename someX>
auto f(someX x) -> std::enable_if_t<std::is_same_v<decltype(x.hidden), int>, int> {return x.hidden;}

this compiles fine with g++, but fails at link time in

int main () {
  X<int> x;
  std::cout << f(x);
}

with

prog.cc:(.text+0x15): undefined reference to `std::enable_if<is_same_v<decltype ({parm#1}.hidden), int>, int>::type f<X<int> >(X<int>)'
collect2: error: ld returned 1 exit status

see here.

What I observed is:

  • when replacing the second argument of enable_if (the type) by something that depends on the class template (intdecltype(x.hidden)) see here linking succeeds with g++.

  • when making hidden public and dropping the friend declaration, the code links fine (so the function template gets instantiated).

  • dropping the enable_if and just declaring the return type as int works fine.

  • move the enable_if from the return type into the template <typename … , typename = typename enable_if_t<…>>, but here I fail to compile because g++ and clang++ tell me friend declarations do not allow default template arguments.

  • drop the enable_if from the friend declaration and only keep it in the definiton → fails to link

  • when compiling with clang++ linking succeeds

  • move the function definition into the class declaration (fails in the real world example, because the function is supposed to take various arguments as variadic template, and then i violate the one-definition-rule, having a f(X<int>, X<float>) defined once in the X<int> definition and once in the X<float> definition.

is this a g++ (8.2) bug or does clang++ violate the standard, and in the latter case, how do I trigger the code generation for the function?


Solution

  • is this a g++ (8.2) bug or does clang++ violate the standard

    I suspect gcc is correct. Template friends are a dark corner of the language.

    how do I trigger the code generation for the function?

    I'd do it via a befriended actor.

    #include <iostream>
    
    struct friend_of_f
    {
        template<class someX> 
        static auto apply(someX x) -> std::enable_if_t<std::is_same_v<decltype(x.hidden), int>, decltype(x.hidden)>
        {
            return x.hidden;
        }
    };
    
    template<typename someX>
    auto f(someX x) -> decltype(friend_of_f::apply(x))
    {
        return friend_of_f::apply(x);
    }
    
    template<typename T>
    struct X 
    {
    
    friend friend_of_f;
    
    private:
      T hidden = 42;
    };
    
    
    
    
    int main () {
      X<int> x;
      std::cout << f(x);
      X<double> y;
    //  std::cout << f(y);
    }