So, the following code builds and runs successfully under clang++ (3.8.0), but fails both under g++ (6.3.0) and vc++ (19.10.24903.0). Both g++ and vc++ complain about redefinition of operator&&.
Does anyone know which compiler is at fault here. For the compilers that fails to compile the code, what would be the workarounds for the compilation error?
#include <functional>
#include <iostream>
template <typename T>
struct awaitable
{
friend awaitable<void> operator&&(awaitable a1, awaitable a2)
{
std::cout << "operator&&(awaitable a1, awaitable a2) - T: " << typeid(T).name() << std::endl;
return awaitable<void>{};
}
template <typename U = T, typename std::enable_if<!std::is_same<U, void>::value>::type* = nullptr>
friend awaitable<void> operator&&(awaitable<void> a1, awaitable<U> a2)
{
std::cout << "operator&&(awaitable<void> a1, awaitable<U> a2) - U: " << typeid(T).name() << std::endl;
return awaitable<void>{};
}
template <typename U = T, typename std::enable_if<!std::is_same<U, void>::value>::type* = nullptr>
friend awaitable<void> operator&&(awaitable<U> a1, awaitable<void> a2)
{
std::cout << "operator&&(awaitable<U> a1, awaitable<void> a2) - U: " << typeid(T).name() << std::endl;
return awaitable<void>{};
}
};
int main(int argc, const char * argv[])
{
awaitable<int> a1, a2, a3, a4;
auto ar = a1 && (a1 && a2) && (a2 && a3) && a4;
}
clang++: http://coliru.stacked-crooked.com/a/cb01926bbcacdfb0
SFINAE works at template instantiation level, ie at struct awaitable<T>
, not at the level of individual members of the template. awaitable<void>
is a valid instantiation and as such it instantiates the declarations of all 3 members of the class, duplicating the latter 2.
It is not that the two definitions conflict with each other - it is that each definition conflicts with itself ( example ) ( more details ).
Workaround 1
Define the helper operators out-of-class (granted, not exactly the same as what you had - these would be friends to any instance of the template)
#include <functional>
#include <iostream>
template <typename T>
struct awaitable
{
friend awaitable<void> operator&&(awaitable a1, awaitable a2)
{
std::cout << "operator&&(awaitable a1, awaitable a2) - T: " << typeid(T).name() << std::endl;
return {};
}
template<typename U>
friend std::enable_if_t<!std::is_void<U>::value, awaitable<void>> operator&&(awaitable<void> a1, awaitable<U> a2);
template<typename U>
friend std::enable_if_t<!std::is_void<U>::value, awaitable<void>> operator&&(awaitable<U> a1, awaitable<void> a2);
};
template<typename U>
std::enable_if_t<!std::is_void<U>::value, awaitable<void>> operator&&(awaitable<void> a1, awaitable<U> a2)
{
std::cout << "operator&&(awaitable<void> a1, awaitable<U> a2) - U: " << typeid(U).name() << std::endl;
return {};
}
template<typename U>
std::enable_if_t<!std::is_void<U>::value, awaitable<void>> operator&&(awaitable<U> a1, awaitable<void> a2)
{
std::cout << "operator&&(awaitable<U> a1, awaitable<void> a2) - U: " << typeid(U).name() << std::endl;
return {};
}
int main(int argc, const char * argv[])
{
awaitable<int> a1, a2, a3, a4;
auto ar = a1 && (a1 && a2) && (a2 && a3) && a4;
}
Workaround 2
Use no SFINAE at all, but a specialization of awaitable
. Note the specialization is reversed - the base implementation is a special case for awaitable<void>
and the specialization is for everything else.
#include <functional>
#include <iostream>
template <typename T, bool isvoid = std::is_void<T>::value>
struct awaitable
{
friend awaitable<void> operator&&(awaitable a1, awaitable a2)
{
std::cout << "operator&&(awaitable a1, awaitable a2) - void" << std::endl;
return {};
}
};
template <typename T>
struct awaitable<T, false>
{
friend awaitable<void> operator&&(awaitable a1, awaitable a2)
{
std::cout << "operator&&(awaitable a1, awaitable a2) - T: " << typeid(T).name() << std::endl;
return {};
}
friend awaitable<void> operator&&(awaitable<void> a1, awaitable<T> a2)
{
std::cout << "operator&&(awaitable<void> a1, awaitable<T> a2) - U: " << typeid(T).name() << std::endl;
return {};
}
friend awaitable<void> operator&&(awaitable<T> a1, awaitable<void> a2)
{
std::cout << "operator&&(awaitable<T> a1, awaitable<void> a2) - void" << std::endl;
return {};
}
};
int main(int argc, const char * argv[])
{
awaitable<int> a1, a2, a3, a4;
auto ar = a1 && (a1 && a2) && (a2 && a3) && a4;
}
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.