I have some trouble forward declaring a function that uses boost::enable_if: the following piece of code gives me a compiler error:
// Declaration
template <typename T>
void foo(T t);
// Definition
template <typename T>
typename boost::enable_if<boost::is_same<T, int> >::type foo(T t)
{
}
int main()
{
foo(12);
return 0;
}
When compiling, I get an "ambiguous call to foo" error. According to the definition of enable_if, the 'type' typedef corresponds to void when the condition is true, so as far as I can see, the two signatures of foo match. Why does the compiler think they are different, and is there a correct way to forward declare foo (preferably without repeating the enable_if part)?
This is not only a problem with enable_if. You get the same error on Visual Studio and gcc with the following code:
struct TypeVoid {
typedef void type;
};
template<typename T>
void f();
template<typename T>
typename T::type f() {
}
int main()
{
f<TypeVoid>();
return 0;
}
I think the main problem is that the return type (before instantiation) is part of the signature of a template function. There is more information here.
Regarding your code, if the declaration refers to the definition, you should match both:
// Declaration
template <typename T>
typename boost::enable_if<boost::is_same<T, int> >::type foo(T t);
// Definition
template <typename T>
typename boost::enable_if<boost::is_same<T, int> >::type foo(T t)
{
}
If the declaration refers to a different function, the compiler would never be able to choose the correct one for ints, because they both are valid. However, you can disable the first one for ints using disable_if:
// Other function declaration
template <typename T>
typename boost::disable_if<boost::is_same<T, int> >::type foo(T t);
// Defition
template <typename T>
typename boost::enable_if<boost::is_same<T, int> >::type foo(T t)
{
}
The problem is that the declaration and the definition do not match.
The solution is that the declaration should contain the exact same signature, and the the enable_if bit.
#include <boost/type_traits/is_same.hpp>
#include <boost/utility/enable_if.hpp>
// Declaration
template <typename T>
typename boost::enable_if<boost::is_same<T, int> >::type foo(T t);
// Definition
template <typename T>
typename boost::enable_if<boost::is_same<T, int> >::type foo(T t)
{
}
int main()
{
foo(12);
return 0;
}
This compiles fine on VC2008.
Related
How can I tell the compiler that U is equivalent to either std::vector<T> or T?
template<typename T, typename U> std::vector<T> foo(T t, U){return std::vector<T>{};}
I'm not sure if this is better or worse than your proposed solution, but you can try template specialization, which is similar to function overloading function overloading.
This would look like this
// Generic function
// UPDATE: you can ignore this function altogether and use the two overloads below
template<typename T, typename U> std::vector<T> foo(T t, U u) { // Do something}
// First specialization for the case of U == T
// UPDATE: this is an *overload*, not a specialization
template<typename T> std::vector<T> foo(T t, T u) { // Do something}
// Second specialization for the case of U == std::vector<T>
// UPDATE: this is an *overload*, not a specialization
template<typename T> std::vector<T> foo(T t, std::vector<T> u) { // Do something}
A possible downside of this approach is the possibility of repeating code, which violates the DRY principle.
As such, if your objective is to simply constraint your arguments without needing to change the function definition, then this answer may be better suited for such application.
Update: As #HolyBlackCat commented, my example was of overloading, and not of template specialization. I updated the text accordingly.
I would disable type deduction (by using depending type) on second argument and provided two overloads.
namespace me {
// c++20 has this, so putting that in own namespace to cover older standards
template<typename T>
strut identity {
using type = T;
};
template<typename T>
using identity_t = typename identityT<>::type;
}
template<typename T>
std::vector<T> foo(T t, const std::vector<me::indentity_t<T>>& v) {
auto copy = v;
copy.push_back(t);
return copy;
}
template<typename T>
std::vector<T> foo(T t, me::indentity_t<T> v) {
return { v, t };
}
I found a solution by myself.
It seems could be achieved by std::enable_if. Is there any better way?
#include <iostream>
#include <vector>
template<typename T, typename U, typename = typename std::enable_if<
std::is_same< T, U >::value || std::is_same<std::vector<T>, U>::value>::type>
std::vector<int> foo()
{
std::cout << "on the move!\n";
return {};
}
int main()
{
foo<int, int>();
foo<int, std::vector<int>>();
//foo<int, float>(); //does not compile, in expectation.
}
A better/harder example:
#include <iostream>
#include <vector>
#include <functional>
template<typename T, typename U, typename = typename std::enable_if<
std::is_same< T, U >::value || std::is_same<std::vector<T>, U>::value>::type>
void foo(std::function<U(void)> func)
{
std::cout << "on the move!\n";
}
int main()
{
foo<int>(std::function<int(void)>());
foo<int>(std::function<std::vector<int>(void)>());
//foo<int>(std::function<float(void)>()); //does not compile, in expectation.
}
I'm currently writing a small library of helper templates and have come across an unexpected inconsistency when using std::enable_if with template parameters.
These function templates compile fine in GCC 7.1:
template<typename T, std::enable_if_t<std::is_same_v<int, T>, T>* = nullptr>
void f() { };
template<typename T, std::enable_if_t<std::is_same_v<double, T>, T>* = nullptr>
void f() { };
int main()
{
f<int>();
f<double>();
}
Whereas these struct templates give compilation errors:
template<typename T, std::enable_if_t<std::is_same_v<int, T>, T>* = nullptr>
struct f { };
template<typename T, std::enable_if_t<std::is_same_v<double, T>, T>* = nullptr>
struct f { };
int main()
{
f<int> x;
f<double> y;
}
The errors:
main.cpp:36:70: error: template parameter ‘std::enable_if_t<is_same_v<int, T>, T>* <anonymous>’
template<typename T, std::enable_if_t<std::is_same_v<int, T>, T>* = nullptr>
^~~~~~~
main.cpp:40:12: error: redeclared here as ‘std::enable_if_t<is_same_v<double, T>, T>* <anonymous>’
struct f { };
^
I'm struggling to understand why with the struct the compiler is complaining about:
There being a default value for the second template parameter.
That the struct is re-declared even though the both have different template parameter lists.
This is due to the fact that functions can be overloaded whereas structs cannot.
For functions, both declarations can coexist and enable_if via SFINAE selects the correct overload.
For structs, the template parameters don't matter. Same name => Redeclaration.
The title seems convoluted but our test-case is actually a minimal example for a real case.
We have code for which we want to choose implementation of a method based on the template parameter. We during clean up we defined conditional enable_if_t with using clause, and as next step wanted to put definition out of line, this produced following code:
#include <type_traits>
#include <iostream>
#include <string>
template <typename T>
struct A {
template <typename U>
using is_int_or_float = std::enable_if_t< std::is_same<U, int>::value || std::is_same<U, float>::value >;
template<typename U = T, is_int_or_float<U>* = nullptr >
void f();
template<typename U = T, typename std::enable_if_t< std::is_same<U,std::string>::value >* = nullptr >
void f();
};
template <typename T>
template <typename U, typename A<T>::template is_int_or_float<U>* >
void A<T>::f(){
std::cout << "f for int or float\n";
}
template <typename T>
template<typename U, typename std::enable_if_t< std::is_same<U,std::string>::value >*>
void A<T>::f() {
std::cout << "f for string\n";
}
int main () {
A<int> a_int;
A<std::string> a_string;
A<float> a_float;
a_int.f();
a_string.f();
a_float.f();
a_float.f<std::string>();
return 0;
}
Gcc does not accept it while clang does: https://godbolt.org/g/Gfm1tw
gcc (and icc) complain that there is no valid prototype for method defined with is_int_or_float.
I am not sure whether clang is too promiscuous or gcc should be able to get past the dependent type.
The code works if we switchtypename A<T>::template is_int_or_float<U> for it's definition:
template <typename T>
template <typename U, std::enable_if_t< std::is_same<U, int>::value || std::is_same<U, float>::value >* >
void A<T>::f(){
std::cout << "f for int or float\n";
}
Does using using change some rules about type matching, or should gcc be able to consume it?
EDIT: Thanks to input from #VTT we figured out, that the difference is in the typename keyword in the out-of-line declaration typename A<T>::template is_int_or_float<U>*. Removing it makes gcc compile and clang refuse the code.
So the question can be rephrased and simplified: Is typename required or this allowed in this case?
I have a class
template <typename T, typename W>
class A {
void foo(W);
void foo(T);
void foo(int);
}
When T=int, W=int, or W=T, this class fails to compile. How can I get the methods to take priority over each other?
I want the priority W > T > int. So if W=T, foo(T) is ignored and foo(W) is called. If T=int, foo(int) is ignored and foo(T) is called.
The compiler is VS2012, but I have Linux too, and will consider GCC/Clang solutions as well. Anything that compiles on any mainstream compiler goes, but only if you say what compilers it works on.
I would tag dispatch. Override dispatching is easy to understand and scales.
We start with a perfect forwarder:
template<class U> void foo(U&&u){
foo( std::forward<U>(u), std::is_convertible<U, W>{}, std::is_convertible<U,T>{} );
}
it creates tag types, in this case true or false types, to dispatch on.
This one:
void foo( W, std::true_type, ... );
catches everything that can convert to W.
Next, we block this one:
void foo( T, std::false_type, std::true_type );
from considerimg cases where the first argument can convert to W.
Finally, this one:
void foo( int, std::false_type, std::false_type );
can only be considered if the first parameter cannot convert to either.
Fancier tag types, or doing the dispatching one at a time, are both possible.
Sorry for typos.
I use a single C++11 feature -- {} to construct an object -- above. If your compiler lacks support for that C++11 feature, simply upgrade your compiler, it is 2014, get with it. Failing that, replace {} with ().
Use std::enable_if:
#include <type_traits>
template <typename T, typename W>
struct A {
void foo(W) {}
template<typename XT=T> typename std::enable_if<std::is_same<XT,T>::value
&& !std::is_same<T, W>::value, void>::type foo(T) {}
template<typename XT=int> typename std::enable_if<std::is_same<XT,int>::value
&& !std::is_same<int, T>::value
&& !std::is_same<int, W>::value, void>::type foo(int) {}
};
Added for testing:
template struct A<short,char>;
template struct A<char,char>;
template struct A<char,int>;
template struct A<int,char>;
template struct A<int, int>;
struct S {};
int main() {
A<S, int>{}.foo(S{});
}
For the relevant part of your template, you could use speclializations:
template <typename U, typename W>
struct Foo
{
void f(U);
void f(W);
};
template <typename T>
struct Foo<T, T>
{
void f(T);
};
For the rest of your class or class template, you can inherit from Foo<A, B> so you can keep the common code out of the part that needs to be specialized:
template <typename A, typename B>
struct TheClass : Foo<A, B>
{
// common code
};
Try template specializations:
template <typename T, typename W>
class A {
void foo(W);
void foo(T);
void foo(int);
};
template <typename T>
class A<T, T> {
void foo(T);
void foo(int);
};
template <>
class A<int, int> {
void foo(int);
};
Here is a solution without specializations of A, but with two helper structures in a few forms.
#include <iostream>
template<typename T, typename W>
struct T_type { typedef T type; };
template<typename W>
struct T_type<W, W> { typedef void* type; /*dummy type*/};
template<typename T, typename W>
struct int_type { typedef int type; };
template<typename W>
struct int_type<int, W> { typedef void** type; /*dummy type*/};
template<typename T>
struct int_type<T, int> { typedef void** type; /*dummy type*/};
template<>
struct int_type<int, int> { typedef void** type; /*dummy type*/};
template<typename T, typename W>
class A {
public:
void foo(W w) {
std::cout << "foo(W)" << std::endl;
}
void foo(typename T_type<T, W>::type t) {
std::cout << "foo(T)" << std::endl;
}
void foo(typename int_type<T, W>::type i) {
std::cout << "foo(int)" << std::endl;
}
};
int main() {
std::cout << "A<float, char>" << std::endl;
A<float, char> a;
a.foo(1.0f);
a.foo('1');
a.foo(1);
std::cout << "A<float, float>" << std::endl;
A<float, float> b;
b.foo(1.0f);
b.foo(1);
std::cout << "A<int, int>" << std::endl;
A<int, int> c;
c.foo(1);
return 0;
}
This is probably a duplicate, but I just can't find one where the OP clearly has the same problem I'm having.
I have a class, and I'm trying to enable operator- only if the class template parameter is not an unsigned type.
#include <type_traits>
template<class T>
struct A {
typename std::enable_if<!std::is_unsigned<T>::value,A>::type operator-() {return {};}
};
int main() {
A<unsigned> a=a;
}
Unfortunately, this produces a compiler error any time I instantiate it with an unsigned type as shown.
main.cpp:5:29: error: no type named 'type' in 'std::enable_if<false, A<unsigned int> >'; 'enable_if' cannot be used to disable this declaration
typename std::enable_if<!std::is_unsigned<T>::value,A>::type operator-() {return {};}
^~~~~~~~~~~~~~~~~~~~~~~~~~~
main.cpp:9:17: note: in instantiation of template class 'A<unsigned int>' requested here
A<unsigned> a=a;
^
Well, I can clearly see that enable_if is not going to work here. One vaguely similar question mentioned I can use inheritance and template specialization to work around this, but... is there really no better way?
I had the same problem once. Turns out there can't be a substitution failure since the default template argument doesn't depend on a template parameter from the function template. You have to have a template argument defaulted to the enclosing template type, like this:
template<typename U = T,
class = typename std::enable_if<!std::is_unsigned<U>::value, U>::type>
A operator-() { return {}; }
A bit long for a comment: You can also use a free function, even for unary operators.
#include <type_traits>
template<class T>
struct A {
};
template<class T>
typename std::enable_if<!std::is_unsigned<T>::value,A<T>>::type
operator-(A<T>) {return {};}
int main() {
A<signed> b;
-b; // ok
A<unsigned> a;
-a; // error
}
This doesn't introduce a member function template for each class template.
Here's how you can befriend it:
template<class T>
class A {
int m;
public:
A(T p) : m(p) {}
template<class U>
friend
typename std::enable_if<!std::is_unsigned<U>::value,A<U>>::type
operator-(A<U>);
};
template<class T>
typename std::enable_if<!std::is_unsigned<T>::value,A<T>>::type
operator-(A<T> p) {return {p.m};}
int main() {
A<signed> b(42);
-b; // ok
A<unsigned> a(42);
//-a; // error
}
You can (should) forward-declare that function template, though.
One possible way is introducing a dummy template parameter:
template<class T>
struct A {
template<
typename D = int,
typename = typename std::enable_if<!std::is_unsigned<T>::value, D>::type
>
A operator-() {return {};}
};
There is a long way using inheritance:
template <class T>
struct A;
template <class T, bool = std::is_unsigned<T>::value>
struct MinusOperator
{
A<T> operator-()
{
A<T>* thisA = static_cast<A<T>*>(this);
// use thisA instead of this to get access to members of A<T>
}
};
template <class T>
struct MinusOperator<T, true> {};
template <class T>
struct A : MinusOperator<T>
{
friend struct MinusOperator<T>;
};