Reputation: 1912
I'm struggling with a friend function for a struct that has a template argument with enable_if
:
// foo.h
#ifndef FOO_H
#define FOO_H
#include <type_traits>
template<
typename T,
typename = typename std::enable_if<std::is_arithmetic<T>::value>::type
>
struct foo {
foo(T bar) : bar(bar) {}
T get() { return bar; }
friend foo operator+(const foo& lhs, const foo& rhs);
// Defining inside a body works:
// {
// return foo(lhs.bar + rhs.bar);
// }
private:
T bar;
};
// None of these work:
// tempate<typename T, typename>
// tempate<typename T>
// tempate<typename T, typename = void>
template<
typename T,
typename = typename std::enable_if<std::is_arithmetic<T>::value>::type
>
foo<T> operator+(const foo<T>& lhs, const foo<T>& rhs)
{
return foo<T>(lhs.bar + rhs.bar);
}
#endif /* ifndef FOO_H */
and
// main.cpp
#include <iostream>
#include "foo.h"
int main()
{
foo<int> f{1};
foo<int> g{2};
std::cout << (f + g).get() << '\n';
return 0;
}
If I try to compile, get the following linker error:
Undefined symbols for architecture x86_64:
"operator+(foo<int, void> const&, foo<int, void> const&)", referenced from:
_main in main-5fd87c.o
ld: symbol(s) not found for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)
(with Apple clang version 11.0.3 (clang-1103.0.32.59)
.)
I want the operator + to only work with types with the same template arguments, e.g., foo only with foo, but not with foo or foo.
I think this is closely related to this question, but I'm having a hard time trying to figure out how to solve my problem.
I tried many template definitions like tempate<typename T, typename>
, tempate<typename T>
, tempate<typename T, typename = typename std::enable_if...>
but none of these work.
As commented in the code, defining inside a body works, but I want to learn how to work with template friend functions with type traits. Any help would be greatly appreciated!
Upvotes: 1
Views: 559
Reputation: 172924
The friend declaration refers to a non-template operator, while the definition out of the class definition refers to a template one, they don't match.
You might want
// forward declaration of the class template
template<
typename T,
typename X = typename std::enable_if<std::is_arithmetic<T>::value>::type
>
struct foo;
// declaration of the operator template
template<
typename T,
typename = typename std::enable_if<std::is_arithmetic<T>::value>::type
>
foo<T> operator+(const foo<T>& lhs, const foo<T>& rhs);
// definition of the class template
template<
typename T,
typename
>
struct foo {
foo(T bar) : bar(bar) {}
T get() { return bar; }
friend foo operator+<T>(const foo& lhs, const foo& rhs);
// or left the template parameters to be deduced as
friend foo operator+<>(const foo& lhs, const foo& rhs);
private:
T bar;
};
//definition of the operator template
template<
typename T,
typename
>
foo<T> operator+(const foo<T>& lhs, const foo<T>& rhs)
{
return foo<T>(lhs.bar + rhs.bar);
}
Upvotes: 1