C++ 我是否正确地排除了基数';s fn()函数?
我正试图做一些有趣的事情,结果突然发生了。我试过了,没有编译或运行时错误。(链接至rextester.com演示)C++ 我是否正确地排除了基数';s fn()函数?,c++,visual-c++,c++14,sfinae,crtp,C++,Visual C++,C++14,Sfinae,Crtp,我正试图做一些有趣的事情,结果突然发生了。我试过了,没有编译或运行时错误。(链接至rextester.com演示) #包括 #包括 int fn1(){return 1;} int fn2(int){return 2;} 模板 类测试库 { 模板 int fn() { 返回FN(); } 公众: int fn_间接法() { 返回static_cast(this)->fn(); } }; 模板 派生类测试:公共测试库 { 公众: 指定的静态constexpr bool fn_=false; };
#包括
#包括
int fn1(){return 1;}
int fn2(int){return 2;}
模板
类测试库
{
模板
int fn()
{
返回FN();
}
公众:
int fn_间接法()
{
返回static_cast(this)->fn();
}
};
模板
派生类测试:公共测试库
{
公众:
指定的静态constexpr bool fn_=false;
};
模板
派生类测试
:公共测试基地
{
使用基础=测试基础;
朋友群;
公众:
指定的静态constexpr bool fn_=真;
int fn()
{
返回FN(1);
}
};
内部主(空)
{
测试_导出x1;
测试_导出的x2;
std::cout您正在使用的构造称为Expression SFINAE。Visual Studio在2017年之前不会向声明。Visual Studio 2015的更新启用了部分支持(请阅读更多信息,)。然而,要使其在所有版本的MSVC中都能正常工作,需要创建不涉及表达式的变通方法,例如在模板专用化参数内。要在您的情况下实现此目的,可以使用模式匹配和附加间接层(例如std::integral_常量):
#包括
#包括
int fn1(){return 1;}
int fn2(int){return 2;}
模板
类测试库
{
模板
int fn()
{
返回FN();
}
公众:
int fn_间接法()
{
返回static_cast(this)->fn();
}
};
模板
派生类测试:公共测试库
{
公众:
指定的静态constexpr bool fn_=false;
};
模板
派生类测试
:公共测试基地
{
使用基础=测试基础;
朋友群;
公众:
指定的静态constexpr bool fn_=真;
int fn()
{
返回fn2(1);
}
};
内部主(空)
{
测试_导出x1;
测试_导出的x2;
std::cout看起来MSVC没有使用test_派生的
部分专门化,您可以通过打印x2.fn_指定的
看到。我看到MSVC 2015“部分支持表达式SFINAE”-我不知道这是否是问题所在。@aschepler,你从哪里得到这句话的?好的,我已经提交了一个bug。如果其他人想提高它的优先级以使它得到修复,那就太好了。但是我在“支持”或“不支持”中都没有看到这种情况示例。我尝试使用VC++2017,至少编译器build 19.10.25019.0能够编译此代码。这意味着VC++2015不会看到此修复。因此,模式匹配在SFINAE失败的地方起作用?有趣的是。SFINAE表达式失败(FN==&fn2
内部专用化)由于MSVC不隐藏他们不支持它。不知道他们没有隐藏事实。)表达式没有失败,我认为这是模板参数列表中的比较。不需要使用间接,只是不要比较指针,使用模式匹配直到C++ 2017。@阿德里安特别注意到[ 1 ]。:我们计划在2015年RTM后立即在编译器中实现表达式SFINAE,并计划在2015年的更新中交付,支持生产使用。(但不一定是2015年的更新1。可能需要更长的时间。)
@Adrian PS comparison是一个表达式,在模板专门化的上下文中,参数是一个表达式sfinae。。。
#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;
}
#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;
}