c++ - 我是否正确地排除了 base 的 fn() 函数?

标签 c++ visual-c++ c++14 sfinae crtp

我正试图做一些有趣的事情,但它在 VC++ 2015 上中断了.我在 clang 试过了和 g++并且没有编译或运行时错误。 (链接到 rextester.com 演示)

#include <type_traits>
#include <iostream>

int fn1()    { return 1; }
int fn2(int) { return 2; }

template <typename FN_T, FN_T FN, typename DERIVED>
class test_base
{
    template <typename T = DERIVED, typename = std::enable_if_t<!T::fn_specified>>
    int fn()
    {
        return FN();
    }

public:
    int fn_indirect()
    {
        return static_cast<DERIVED*>(this)->fn();
    }
};

template <typename FN_T, FN_T FN, typename ENABLER = void>
class test_derived : public test_base<FN_T, FN, test_derived<FN_T, FN>>
{
public:
    static constexpr bool fn_specified = false;
};

template <typename FN_T, FN_T FN>
class test_derived<FN_T, FN, std::enable_if_t<FN == &fn2>>
    : public test_base<FN_T, FN, test_derived<FN_T, FN>>
{
    using base = test_base<FN_T, FN, test_derived<FN_T, FN>>;
    friend base;

public:
    static constexpr bool fn_specified = true;
    int fn()
    {
        return FN(1);
    }
};

int main(void)
{
    test_derived<decltype(&fn1), &fn1> x1;
    test_derived<decltype(&fn2), &fn2> x2;

    std::cout << x1.fn_indirect() << " ";

    // comment next line out and it'll work in VC++
    std::cout << x2.fn_indirect() << std::endl;
    return 0;
}

思路是,如果我在派生类中指定一个fn(),那么基类就不会实例化该函数,这样调用时就不会因为参数过多而报错FN(),当 FN() 指向特定函数时(在本例中为 fn2(int))。

我这样做是否正确,还是我遗漏了什么?

最佳答案

您使用的结构称为 Expression SFINAE。 Visual Studio 直到 2017 年才声明为 support it . Visual Studio 2015 的更新启用了部分支持(阅读更多关于 update 1updates 1, 2 and 3 的信息)。然而,要使其在所有版本的 MSVC 中工作,需要创建不涉及表达式的变通方法,例如内部模板特化参数。要在您的情况下实现它,可以使用带有附加间接层的模式匹配(例如 std::integral_constant):

#include <type_traits>
#include <iostream>

int fn1()    { return 1; }
int fn2(int) { return 2; }

template <typename FN_T, FN_T FN, typename DERIVED>
class test_base
{
    template <typename T = DERIVED, typename = std::enable_if_t<!T::fn_specified>>
    int fn()
    {
        return FN();
    }

public:
    int fn_indirect()
    {
        return static_cast<DERIVED*>(this)->fn();
    }
};

template <class FN_T>
class test_derived : public test_base<typename FN_T::value_type, FN_T::value, test_derived<FN_T>>
{
public:
    static constexpr bool fn_specified = false;
};

template <typename FN_T>
class test_derived<std::integral_constant<FN_T, &fn2>>
    : public test_base<FN_T, &fn2, test_derived<std::integral_constant<FN_T, &fn2>>>
{
    using base = test_base<FN_T, &fn2, test_derived<std::integral_constant<FN_T, &fn2>>>;
    friend base;

public:
    static constexpr bool fn_specified = true;
    int fn()
    {
        return fn2(1);
    }
};

int main(void)
{
    test_derived<std::integral_constant<decltype(&fn1), &fn1>> x1;
    test_derived<std::integral_constant<decltype(&fn2), &fn2>> x2;

    std::cout << x1.fn_indirect() << " ";

    // comment next line out and it will work
    std::cout << x2.fn_indirect() << std::endl;
    return 0;
}

[live demo]

关于c++ - 我是否正确地排除了 base 的 fn() 函数?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/44599692/

相关文章:

c++ - SDKDDKVer.h 是做什么用的?

c++ - 如何在 C/C++ 中临时禁用宏扩展?

c++ - 收到 C2628 错误

c++ - 如何在 C++ 的外部类构造函数中初始化嵌套类成员

c++ - make_unique 给出错误 2248

c++ - 自定义类型 Qlist 和范围

c++ - 排序数组文件 I/O

c++ - 将 vector 传递给函数

c++ - 可变数量的参数

C++ 使用结构返回多个值