C++ 友元函数模板重载和 SFINAE 在 clang++、g++、vc++(C++14 模式)中的不同行为
C++ friend function template overloading and SFINAE different behaviors in clang++, g++, vc++ (C++14 mode)
因此,以下代码在 clang++ (3.8.0) 下构建并成功运行,但在 g++ (6.3.0) 和 vc++ (19.10.24903.0) 下均失败。 g++ 和 vc++ 都抱怨 operator&& 的重新定义。
有谁知道是哪个编译器出了问题。对于无法编译代码的编译器,编译错误的解决方法是什么?
#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 在模板实例化级别工作,即在 struct awaitable<T>
,而不是在模板的各个成员级别。 awaitable<void>
是一个有效的实例化,因此它实例化了 class 的所有 3 个成员的声明,复制了后者 2.
并不是两个定义互相冲突,而是每个定义都自相矛盾(example) (more details)。
解决方法 1
定义辅助运算符 out-of-class(当然,与您所拥有的不完全相同 - 这些将是模板的 any 实例的朋友)
#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;
}
解决方法 2
完全不使用 SFINAE,而是 awaitable
的专业化。请注意,专业化是相反的 - 基本实现是 awaitable<void>
的特例,专业化适用于其他一切。
#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;
}
因此,以下代码在 clang++ (3.8.0) 下构建并成功运行,但在 g++ (6.3.0) 和 vc++ (19.10.24903.0) 下均失败。 g++ 和 vc++ 都抱怨 operator&& 的重新定义。
有谁知道是哪个编译器出了问题。对于无法编译代码的编译器,编译错误的解决方法是什么?
#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 在模板实例化级别工作,即在 struct awaitable<T>
,而不是在模板的各个成员级别。 awaitable<void>
是一个有效的实例化,因此它实例化了 class 的所有 3 个成员的声明,复制了后者 2.
并不是两个定义互相冲突,而是每个定义都自相矛盾(example) (more details)。
解决方法 1
定义辅助运算符 out-of-class(当然,与您所拥有的不完全相同 - 这些将是模板的 any 实例的朋友)
#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;
}
解决方法 2
完全不使用 SFINAE,而是 awaitable
的专业化。请注意,专业化是相反的 - 基本实现是 awaitable<void>
的特例,专业化适用于其他一切。
#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;
}