使用 decltype() 和 SFINAE 时出错

发布于 2024-10-02 00:22:04 字数 1547 浏览 6 评论 0原文

为了回答..某个地方的其他问题,我编写了这段代码。

struct no_type{};
template<typename T> struct has_apply {
    static decltype(T().apply<0u>(double())) func( T* ptr );
    static no_type func( ... );
    static const bool result = !std::is_same<no_type, decltype(func(nullptr))>::value;
};

class A {
public:
    template< unsigned n >
    void apply( const double& );

};
class B {
};

int main()
{
  std::cout << std::boolalpha << has_apply< A >::result << '\n';
  std::cout << std::boolalpha << has_apply< B >::result << '\n';
  std::cin.get();
  return( 0 );
}

现在在我看来,如果 T 提供接受双右值和模板参数文字的非静态成员函数“apply”,则结果应该为 true,否则为 false。然而,在编译 has_apply 时,给出的示例实际上无法针对 B 类进行编译。 decltype 语句中 T 的替换失败是否意味着它只是调用另一个函数?这不就是SFINAE的宗旨吗?

以有史以来最荒谬、最无意义的方式解决:

struct no_type{};
template<typename T> struct has_apply {
    template<typename U> static decltype(U().apply<0u>(double())) func( U* );
    template<typename U> static no_type func( ... );
    static const bool result = !std::is_same<no_type, decltype(func<T>(nullptr))>::value;
};

class A {
public:
    template< unsigned n >
    void apply( const double& );

};
class B {
};

int main()
{
  std::cout << std::boolalpha << has_apply< A >::result << '\n';
  std::cout << std::boolalpha << has_apply< B >::result << '\n';
  std::cin.get();
  return( 0 );
}

In response to .. some other question somewhere, I wrote this code.

struct no_type{};
template<typename T> struct has_apply {
    static decltype(T().apply<0u>(double())) func( T* ptr );
    static no_type func( ... );
    static const bool result = !std::is_same<no_type, decltype(func(nullptr))>::value;
};

class A {
public:
    template< unsigned n >
    void apply( const double& );

};
class B {
};

int main()
{
  std::cout << std::boolalpha << has_apply< A >::result << '\n';
  std::cout << std::boolalpha << has_apply< B >::result << '\n';
  std::cin.get();
  return( 0 );
}

Now it seems to me that result should be true if T offers a non-static member function "apply" that accepts a double rvalue and a template parameter literal, and false otherwise. However, the example given actually fails to compile for class B, when compiling has_apply<B>. Shouldn't the fact that the substitution of T failed in the decltype statement mean that it simply calls the other func? Isn't that kind of the point of SFINAE?

Solved in the most ridiculous, pointless fashion ever:

struct no_type{};
template<typename T> struct has_apply {
    template<typename U> static decltype(U().apply<0u>(double())) func( U* );
    template<typename U> static no_type func( ... );
    static const bool result = !std::is_same<no_type, decltype(func<T>(nullptr))>::value;
};

class A {
public:
    template< unsigned n >
    void apply( const double& );

};
class B {
};

int main()
{
  std::cout << std::boolalpha << has_apply< A >::result << '\n';
  std::cout << std::boolalpha << has_apply< B >::result << '\n';
  std::cin.get();
  return( 0 );
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

猥︴琐丶欲为 2024-10-09 00:22:04

当函数模板的模板参数替换失败时,SFINAE 适用,而不适用于具有相关(非模板)函数作为成员的类模板的模板参数,就像您的情况一样。

修复此问题后,您至少应该将 decltype(T().apply<0u>(double())) 更改为 decltype(T().template apply<0u>(double( ))) 因为 T() 表达式是依赖类型。原因是这样的:当编译器第一次看到 T().apply<0u> 时,它还不知道 T 的任何信息,那么它应该如何解析. 之后的标记 apply<apply 可能是一个成员模板,然后 < 将为其启动参数列表。 OTOH apply 可能是非模板成员(例如数据成员),然后 < 将被解析为“小于”运算符。存在歧义,编译器此时解决该问题还为时过早。程序员需要一种消歧机制来告诉编译器 apply 应该是什么:是否是模板。这里出现了 .template (或 ->template,或 ::template)构造来救援:如果它存在,编译器知道它应该是模板成员,否则如果它不存在,则编译器知道该成员不应该是模板。

最后,这是我创建的一个示例,它可以在 g++ 4.5.0 上使用 -std=c++0x 正常工作并产生所需的结果:

#include <iostream>

template < class T >
decltype( T().template apply< 0u >( double() ) ) f( T &t )
{
    return t.template apply< 0u >( 5. );
}

const char *f( ... )
{
    return "no apply<>";
}

class A {
public:
    template < unsigned >
    int apply( double d )
    {
        return d + 10.;
    }
};

class B {};

int main()
{
    A a;
    std::cout << f( a ) << std::endl;
    B b;
    std::cout << f( b ) << std::endl;
}

输出为:

15
no apply<>

现在,如果您删除两个 .template 从第一个 f() 定义中,则输出变为:

no apply<>
no apply<>

这表示 class A 的替换失败,因为它没有任何非名为 apply 的模板成员。斯菲尼在行动!

SFINAE applies when substitution fails for a function template's template parameter, not for a class template's template parameter that has the (non-template) function in question as a member, as is in your case.

After fixing that, you should at least change decltype(T().apply<0u>(double())) to decltype(T().template apply<0u>(double())) because T() expression is of a dependent type. The reason for that is this: when the compiler first sees T().apply<0u>, it doesn't know anything about T yet, so how should it parse the tokens apply and < after .? apply might be a member template, and then < would start the argument list for it. OTOH apply might instead be a non-template member (e.g. a data member), and then < would be parsed as 'less-than' operator. There is an ambiguity, and it's still too early for the compiler to resolve that at this point. There is a need for a disambiguation mechanism a programmer could use to tell the compiler what apply is expected to be: a template or not. And here comes the .template (or ->template, or ::template) construct to the rescue: if it's present, the compiler knows it should be a template member, otherwise if it's not present then the compiler knows the member shouldn't be a template.

Finally here's an example I created that works correctly and produces the desired results on g++ 4.5.0 with -std=c++0x:

#include <iostream>

template < class T >
decltype( T().template apply< 0u >( double() ) ) f( T &t )
{
    return t.template apply< 0u >( 5. );
}

const char *f( ... )
{
    return "no apply<>";
}

class A {
public:
    template < unsigned >
    int apply( double d )
    {
        return d + 10.;
    }
};

class B {};

int main()
{
    A a;
    std::cout << f( a ) << std::endl;
    B b;
    std::cout << f( b ) << std::endl;
}

Output is:

15
no apply<>

Now if you remove both .template from the first f() definition, then the output becomes:

no apply<>
no apply<>

Which is to indicate substitution failure for class A as it doesn't have any non-template member named apply. SFINAE in action!

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文