我是否正确排除了基数的fn()函数?

时间:2017-06-17 00:41:03

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

我试图做一些有趣的事情,并在VC++ 2015打破了。我在clangg++中尝试了它,没有编译或运行时错误。 (链接到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))。

我这样做是正确的,还是我错过了什么?

1 个答案:

答案 0 :(得分:0)

您正在使用的构造称为表达式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]