C++ 为什么这个C++;检测类型T是否有无效运算符(EDT const&;失败)的特征?
我试图使用SFINAE来检测作为模板参数T传递的类型是否有一个T::operator()(p const&),其中p也是一个模板参数。不幸的是,我无法让操作符()工作,尽管我可以让它为一个普通方法工作 下面是一些示例代码,演示了我面临的问题:C++ 为什么这个C++;检测类型T是否有无效运算符(EDT const&;失败)的特征?,c++,metaprogramming,template-meta-programming,C++,Metaprogramming,Template Meta Programming,我试图使用SFINAE来检测作为模板参数T传递的类型是否有一个T::operator()(p const&),其中p也是一个模板参数。不幸的是,我无法让操作符()工作,尽管我可以让它为一个普通方法工作 下面是一些示例代码,演示了我面临的问题: #include <iostream> #include <iomanip> #include <utility> #include <type_traits> using namespace std;
#include <iostream>
#include <iomanip>
#include <utility>
#include <type_traits>
using namespace std;
struct has
{
void operator()(int const&);
};
struct hasNot1
{
void operator()(int);
};
struct hasNot2
{
void operator()();
};
struct hasNot3
{
void operator()(float);
};
struct hasNot4
{
};
template<typename T, typename EDT>
struct is_callable_oper
{
private:
typedef char(&yes)[1];
typedef char(&no)[2];
template <typename U, void (U::*)(EDT const &)> struct
Check;
template<typename>
static yes test(...);
template <typename U>
static no
test(Check<U, &U::operator()>*);
public:
static constexpr bool value = sizeof(test<T>(0))
== sizeof(yes);
};
int main() {
cout << boolalpha << is_callable_oper<has, int&>::value << " "
<< is_callable_oper<has, int>::value << " "
<< is_callable_oper<hasNot1, int&>::value << " "
<< is_callable_oper<hasNot2, int&>::value << " "
<< is_callable_oper<hasNot3, int&>::value << " "
<< is_callable_oper<hasNot4, int&>::value << endl;
return 0;
}
#包括
#包括
#包括
#包括
使用名称空间std;
结构有
{
void运算符()(int const&);
};
结构hasNot1
{
void操作符()(int);
};
结构hasNot2
{
void运算符();
};
结构hasNot3
{
void操作符()(float);
};
结构hasNot4
{
};
模板
结构是可调用的
{
私人:
typedef字符(&yes)[1];
typedef字符(&no)[2];
模板结构
检查;
模板
静态是测试(…);
模板
静态否
测试(检查*);
公众:
静态constexpr bool值=sizeof(测试(0))
==sizeof(是);
};
int main(){
cout在更现代的编译器上,解决这类问题的典型方法是void\t
习惯用法:。它是这样运行的。步骤1:
template <class T>
using void_t = void; // avoid variadics for simplicity
对你的类型来说是合理的。这基本上就是你想要的特质检测。注意,有一些微妙之处涉及左值、右值、常量,但这将有点涉及到这里
现在,正如Sam在注释中所指出的,这个表达式是否合理包括隐式转换。因此,输出确实为true false。工作示例:
例如,您期望is\u callable\u oper::value
为false。但是,显然可以使用int
调用接受常量int&
的函数。因此,您得到的是true。您澄清了运算符()
返回一个void
,您希望严格匹配签名,忽略类型转换
如果是这样,那么您的预期结果应该是false-true-false
,而不是true-false-false
:
is_callable_oper<has, int&>::value
由于has
确实有一个操作符()
接受常量int&
参数,因此该测试应该通过
我的解决方案只是使用std::is_same
来比较两种类型,如果std::enable_
使模板解析候选失败,则使用std::enable_
#include <type_traits>
#include <iostream>
struct has
{
void operator()(int const&);
};
struct hasNot1
{
void operator()(int);
};
struct hasNot2
{
void operator()();
};
struct hasNot3
{
void operator()(float);
};
struct hasNot4
{
};
template<typename T, typename P, typename foo=void>
class is_callable_oper : public std::false_type {};
template<typename T, typename P>
class is_callable_oper<T, P, typename std::enable_if<
std::is_same<decltype(&T::operator()),
void (T::*)(const P &)>::value>::type>
: public std::true_type {};
int main() {
std::cout << std::boolalpha << is_callable_oper<has, int&>::value << " "
<< is_callable_oper<has, int>::value << " "
<< is_callable_oper<hasNot1, int&>::value << " "
<< is_callable_oper<hasNot2, int&>::value << " "
<< is_callable_oper<hasNot3, int&>::value << " "
<< is_callable_oper<hasNot4, int&>::value << std::endl;
return 0;
}
你真的不可能得到一个更新的编译器吗?TMP在新版本的语言中非常简单。我可以用新的编译器用5行代码为你解决这个问题。我现在只能用这一行。我希望看到你的解决方案使用更现代的编译器;请与我分享。这里有一个非常微妙的问题。我如果一个函数的参数是,比如说一个float
,你可以传递一个int&
,因为它可以转换为一个float
。你需要决定这是否是“可调用的”对于您来说,也可以不这样。在这种情况下,使用decltype
和declval
将导致您的操作员被视为可调用。如果您希望严格检查操作员签名,则只有在您还指定了操作员的返回类型时,才可以这样做。如果您使用decltype
和declval
,则正确的结果你的测试程序的lts应该是true-false
@Sam Varshavchik,所以转换规则让我很头疼;很高兴知道。我到底应该如何使用decltype和declval来改进这一点?我的返回类型是fixed->void。我如何利用它?谢谢你的帮助!谢谢你的详细回答。我会花点时间仔细考虑一下在问任何问题之前,我一直想知道是否有任何方法来限制类型转换。@ BATBRAT山姆的答案可能比你想要的更好。虽然,我会说,这是一个有XY问题的情况。在C++中,你通常关心你是否能做些事情,例如是否有东西可以调用。某种类型。不是签名是否完全匹配。从某种意义上说,这样做可能会破坏隐式转换的契约。你完全正确-我不需要限制完全匹配;事实上,我不应该。比我的解决方案更好,考虑到OP要求的。而不是常量int&
,int&const&
…也就是说int&
。这对可重载的调用运算符也不起作用。@SamVarshavchik,为什么int&const&会松开const?这就是不匹配的原因,不是吗?我也很好奇,如果不进行精确匹配,如何实现此测试-正如Nir Friedman正确指出的,我应该这样做ldn没有进行精确匹配。我对问题进行了编辑以反映这一点。你需要发布一个修改后的问题,该问题最少但明确地阐述了你试图实现的目标,最重要的是,显示了你已经完成的工作,并解释了你缺少的内容(这样你的问题就不会只是同义词)“请为我写代码”).现在,这个问题变得太复杂,很难理解。你应该从头开始。@SamVarshavchik观点很好。我将删除编辑并接受你的答案;然后,我将从头开始,正确地问一个问题。我绝对不希望任何人为我写代码:整个要点是理解我有足够的时间独自做这件事。
std::declval<T>()(declval<P>())
is_callable_oper<has, int&>::value
is_callable_oper<has, int>
#include <type_traits>
#include <iostream>
struct has
{
void operator()(int const&);
};
struct hasNot1
{
void operator()(int);
};
struct hasNot2
{
void operator()();
};
struct hasNot3
{
void operator()(float);
};
struct hasNot4
{
};
template<typename T, typename P, typename foo=void>
class is_callable_oper : public std::false_type {};
template<typename T, typename P>
class is_callable_oper<T, P, typename std::enable_if<
std::is_same<decltype(&T::operator()),
void (T::*)(const P &)>::value>::type>
: public std::true_type {};
int main() {
std::cout << std::boolalpha << is_callable_oper<has, int&>::value << " "
<< is_callable_oper<has, int>::value << " "
<< is_callable_oper<hasNot1, int&>::value << " "
<< is_callable_oper<hasNot2, int&>::value << " "
<< is_callable_oper<hasNot3, int&>::value << " "
<< is_callable_oper<hasNot4, int&>::value << std::endl;
return 0;
}
template<typename T, typename P>
class is_callable_oper<T, P,
std::void_t<decltype( std::declval< void (T::*&)(const P &)>()=&T::operator())>>
: public std::true_type {};