Reputation: 3613
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
What I observed is:
when replacing the second argument of enable_if
(the type) by something that depends on the class template (int
→ decltype(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?
Upvotes: 1
Views: 97
Reputation: 69882
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);
}
Upvotes: 1