我有两种类型,,,CustomNotCompatibleWithRun
和CustomCompatibleWithRun
一个run()
函数:
#include <iostream>
#include <type_traits>
template <typename T_>
struct CustomNotCompatibleWithRun { using T = T_; };
template <typename T_>
struct CustomCompatibleWithRun {
using T = T_;
CustomCompatibleWithRun operator+(const CustomCompatibleWithRun& other)
{
CustomCompatibleWithRun res;
res.a = a + other.a;
res.b = b + other.b;
return res;
}
void print() const
{
std::cout << "a, b = " << a << ", " << b << "\n";
}
T a;
T b;
};
template <typename T>
T run(T a, T b) { return a+b; }
我想编写一个类型特征,IsTypeCompatibleWithRun
用于检查给定类型Type
(其唯一约定是具有类型别名Type::T
)是否可以在函数中使用run()
。即:
int main() {
using T = float;
CustomCompatibleWithRun<T> obj1{.a = 1.f, .b = 2.f};
CustomCompatibleWithRun<T> obj2{.a = 2.f, .b = 3.f};
obj1.print(); // a, b = 1, 2
obj2.print(); // a, b = 2, 3
const auto obj3 = obj1 + obj2;
obj3.print(); // a, b = 3, 5
const auto obj4 = run(obj1, obj2);
obj4.print(); // a, b = 3, 5
CustomNotCompatibleWithRun<T> obj5{};
CustomNotCompatibleWithRun<T> obj6{};
// const auto obj7 = obj5 + obj6; // does not compile
std::cout << "IsTypeCompatibleWithRun<CustomCompatibleWithRun>::value: " << IsTypeCompatibleWithRun<CustomCompatibleWithRun<T>>::value << "\n"; // should print 1
std::cout << "IsTypeCompatibleWithRun<CustomNotCompatibleWithRun>::value: " << IsTypeCompatibleWithRun<CustomNotCompatibleWithRun<T>>::value << "\n"; // should print 0
}
我尝试了以下三个选项:
template <typename Type, typename = void>
struct IsTypeCompatibleWithRun : std::false_type {};
// Option 1
template <typename Type>
struct IsTypeCompatibleWithRun<Type, decltype(void(run<typename Type::T>(std::declval<typename Type::T>(), std::declval<typename Type::T>())))> : std::true_type {};
// Option 2
template <typename Type>
struct IsTypeCompatibleWithRun<Type, decltype(void(std::declval<Type>() + std::declval<Type>()))> : std::true_type {};
// Option 3
template <typename Type>
struct IsTypeCompatibleWithRun<Type, std::void_t<decltype(run(std::declval<Type>(), std::declval<Type>()))>> : std::true_type {};
只有选项 2 有效——据我理解,这是因为我们强制将+
运算符置于类型特征本身内部,而对于选项 1 和 3,只run()
检查函数的签名?然而,我对选项 2 并不满意,因为它需要将实际的函数体重复run()
到类型特征本身中。在这个简单的例子中,这样做没问题,但在我的应用程序中,函数体run()
要复杂得多,不可能在类型特征内部重复它。
然后我尝试将签名更改run()
为
template <typename T>
auto run(T a, T b)
但代码根本编译不出来。于是我尝试添加一个尾随返回类型
template <typename T>
auto run(T a, T b) -> decltype(a+b)
现在选项 3 可以工作,但选项 1 不行。这是为什么呢?
无论哪种方式,上述操作都涉及重复a+b
尾随返回类型中的部分,因此这对我来说不是一个解决方案。
在 C++ 14 中,有没有一种方法可以在不改变run()
而仅依赖run()
(并且不重复其函数体)的情况下实现这种类型特征?
您看到的是 C++14 中 SFINAE (替换失败不是错误) 的限制。它仅在模板替换期间检查函数签名,而不检查函数体(如果这样做合理的话)。
如果不改变
run()
或重复其主体,就无法实现这种类型特征。希望这有帮助
稍微解释一下你对上面 Alex 的回答的问题。
这个限制可以在 C++ 20 中通过概念来克服。这意味着在 C++ 14 中,使用 option1 并在 run() 的声明中使用 std::enable_if<> 来进一步限制对 run() 的访问是完全可能的。虽然在 C++ 14 中看起来会有点混乱,但可以实现。
例子:
程序输出:
或者,您可以检查类级别的功能:
您可以在此处使用此代码:https://godbolt.org/z/vnWd6vcnc