C++ SFINAE with CRTP, G++ 编译错误

标签 c++ sfinae crtp

我想知道下面的代码是否有效。

我的初衷是,我喜欢一个基类,它可以将对某个成员的调用分派(dispatch)给派生类成员(如果派生类成员存在),或者如果派生类没有该成员则退回到默认行为。另一个用途是这个基类可以自己使用,Derived模板参数成为一个实现策略。无论如何,以下 MWE 编译并正确运行 clang++ , 英特尔, icpc和MSVS。但是它失败了 g++ (从 4.4 到 4.6,我接触过的任何版本)在问题末尾有错误消息。

如果我更改 call在点 (1)、(2)、(3) 到 call_dispatch (这是我最初做的那种事情),g++不再提示了。我认为让调度函数和调用者同名不是一个好习惯。我只是很好奇它是否会起作用,并且好奇地尝试了一下(我不知道这个想法是怎么来的)。我的理由是,在品脱 (1) 时,call是用一个参数调用的,所以重载决议将不匹配它的调用者,即零参数一。它不会匹配 SFINAE一个在第 (2) 点,因为 D2没有成员,那么它应该匹配第(3)点的成员。就像 (1)-(3) 被命名为 call_dispatch 的情况一样.

但是g++不同意我和其他编译器。那么,这是否是 g++ 的错误实现?还是代码本身无效?除了错误信息真的很困惑。 void (B<D2>::*)() 在哪里和 &B<D2>::call来自?他调用的int成员指针被定义为D2的成员。

#include <iostream>
#include <functional>

template <typename Derived>
class B
{
    public :

    void call ()
    {
        call<Derived>(0); //----------------------------------------- (1)
    }

    private :

    template <typename D, void (D::*)()> class SFINAE {};

    template <typename D>
    void call (SFINAE<D, &D::call> *) //---------------------------- (2)
    {
        static_cast<Derived *>(this)->call();
    }

    template <typename D>
    void call (...) //--------------------------------------------- (3)
    {
        std::cout << "Call B" << std::endl;
    }
};

class D1 : public B<D1>
{
    public :

    void call ()
    {
        std::cout << "Call D1" << std::endl;
    }
};

class D2 : public B<D2> {};

int main ()
{
    D1 d1;
    D2 d2;
    d1.call();
    d2.call();

    return 0;
}

错误:

foo.cpp: In member function ‘void B<Derived>::call() [with Derived = D2]’:
foo.cpp:48:13:   instantiated from here
foo.cpp:11:9: error: ‘&B<D2>::call’ is not a valid template argument for type ‘void (D2::*)()’ because it is of type ‘void (B<D2>::*)()’
foo.cpp:11:9: note: standard conversions are not allowed in this context

编辑

虽然我还没有完全理解上面的代码哪里出了问题。但我认为还有另一种方法,无需专门构造 SFINAE 类,但可以实现相同的效果。

#include <iostream>

template <typename Derived>
class B
{
    public :

    void call ()
    {
        call_dispatch(&Derived::call);
    }

    template <typename C>
    void call_dispatch (void (C::*) ())
    {
        static_cast<Derived *>(this)->call();
    }

    void call_dispatch (void (B<Derived>::*) ())
    {
        std::cout << "Call B" << std::endl;
    }

    private :
};

class D1 : public B<D1>
{
    public :

    void call ()
    {
        std::cout << "Call D1" << std::endl;
    }
};

class D2 : public B<D2> {};

int main ()
{
    D1 d1;
    D2 d2;

    d1.call();
    d2.call();

    return 0;
}

基本上,因为 D1D2两者都来自 B , 所以表达式 &Derived::call总会解决的。在 D1它解决了&D1::call , 然后使用模板版本成员。在 D2 , 它没有自己的 call , 所以 &D2::call解析为 &B::call , 感谢
@DavidRodríguez-dribeas,他现在指出 &D2::call类型为 B::call ,因此模板和非模板成员同样匹配,但首选非模板。所以使用默认调用。

能帮我看看这个新代码有没有缺陷?

最佳答案

Where does the void (B::*)() and &B::call come from?

指向成员的指针的类型不是您获得该指针的类型,而是定义成员的类型。

struct base { int x; };
struct derived : base {};
int main() {
   std::cout << std::is_same< decltype(&derived::x), int (base::*) >::value << std::endl;
}

上面的程序打印1 .在您的情况下,当您使用 &D::base , 编译器找到 B<D2>::call作为基本模板的成员,这是表达式的结果:void (B<D2>::*)() .

关于C++ SFINAE with CRTP, G++ 编译错误,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/11042313/

相关文章:

c++ - 二维数组中的用户输入 (C++)

c++ - 检查类是否具有给定签名的成员函数

C++ 模板友元运算符与另一个类似的运算符

C++ - 循环依赖(在模板化基类中使用子类的内部类型)

c++ - 奇怪的重复模板模式多态拷贝中的继承 (C++)

c++ - glGenFramebuffers 或 glGenFramebuffersEXT?

c++ - 为什么我的 C++ Boost ASIO HTTP 客户端返回不完整的响应?

c++ - 如何在 xrgb 图像数据之上合成 argb 图像数据

c++ - 检查函数模板是否是一元的

c++ - 以 CRTP 为左值的表达式模板