为什么赢得Visual Studio让我在enable_if中使用Templatized,constexpr函数?

时间:2017-01-11 14:36:11

标签: c++ c++11 templates visual-studio-2015 constexpr

所以我把它归结为最小的,完整的,可验证的示例,看起来Visual Studio 2015似乎不允许我在{{{{{}}中使用模板化的constexpr函数1}}。

例如:

enable_if

给我错误:

  

错误C2995:template <typename T> constexpr bool condition() { return sizeof(T) > 1; } :功能模板已定义

当我尝试在替换中使用它时,失败不是像这样的错误编译:

enable_if<_Test,T>::type test(void)

这在gcc中运行良好:http://ideone.com/m9LDdS
如果我删除了template <typename T> enable_if_t<condition<T>()> test() { cout << "true\n"; } template <typename T> enable_if_t<!condition<T>()> test() { cout << "false\n"; } 的模板化,它在Visual Studio 2015中运行良好。我相信在中引入了condition函数,为什么Visual Studio 2015不支持此功能?这是一个错误吗?

4 个答案:

答案 0 :(得分:6)

问题似乎是MSVC14 / VS2015无法正确解析SFINAE表达式并结合constexpr函数的返回值作为模板参数。

作为一种解决方法,您可以将constexpr的返回值分配给一个“静态常量”&#39;结构的成员,并使用此成员作为模板参数。

#include <type_traits>
#include <iostream>

using std::enable_if_t;
using std::cout;

template <typename T>
constexpr bool condition() { return sizeof(T) > 1; }

template <typename T>
struct condition_ { static const bool value = condition<T>();};

template <typename T>
enable_if_t<condition_<T>::value> test() { cout << "true\n"; }
template <typename T>
enable_if_t<!condition_<T>::value> test() { cout << "false\n"; }

int main() {
    test<int>();
    test<bool>();
    return 0;
}

http://rextester.com/VVNHB62598

您还在评论中提到,您的实际问题出现在另一个案例中而不是您的MCVE(How can I Initialize a div_t Object?

对于这种情况,解决方法可能如下所示:

#include <type_traits>
#include <cstdlib>
#include <utility>


template <typename T>
using divtype = decltype(std::div(std::declval<T>(), std::declval<T>()));


template <typename T>
struct condition
{
    static const bool value = divtype<T>{ 1, 0 }.quot != 0;
};


template <typename T>
std::enable_if_t<condition<T>::value, divtype<T>> make_div(const T quot, const T rem) { return{ quot, rem }; }

template <typename T>
std::enable_if_t<!condition<T>::value, divtype<T>> make_div(const T quot, const T rem) { return{ rem, quot }; }

int main() {

    make_div<int>(1, 2);
    return 0;
}

http://rextester.com/ULDFM22040

根据此Visual Studio C++ Team blog entry,VS2015还没有(完全)支持Expression SFINAE

  

[1]我们计划在2015 RTM之后立即开始在编译器中实现Expression SFINAE,并且我们计划在2015年更新中提供它,支持生产使用。 (但不一定是2015年更新1.可能需要更长时间。)

答案 1 :(得分:3)

问题是您最终得到了两种形式的test模板:

template<class>void test()

并且编译器抱怨。这可能与表达SFINAE失败有关,它不能及早评估condition<T>()的表达,或以其他方式失败。

这是一种解决方法:

template<std::size_t>
struct counter{ enum type{}; };

template<std::size_t N>
using counter_type=typename counter<N>::type;

template <typename T>
constexpr bool condition() { return sizeof(T) > 1; }

template <class T,counter_type<0>...,class=std::enable_if_t<condition<T>()>>
void test() { std::cout << "true\n"; }

template <class T,counter_type<1>...,class=std::enable_if_t<!condition<T>()>>
void test() { std::cout << "false\n"; }

现在两个不同的test的模板签名不同,而condition<T>()的{​​{1}}表达式的表达式不正确也不会导致问题。

答案 2 :(得分:2)

@ Yakk回答的另一个选择:

template <class T, std::enable_if_t<(sizeof(T) > 1)>* = nullptr>
void test() { std::cout << "true\n"; }

template <class T, std::enable_if_t<!(sizeof(T) > 1)>* = nullptr>
void test() { std::cout << "false\n"; }
编辑:刚看到这基本上是TC在评论中指出的解决方案。

EDIT2:更正了在MSVC2015中编译的代码,请参阅注释。

答案 3 :(得分:1)

这是一个已知的MSVC问题,并在其中一篇博文中提到过。发生了什么事情,编译器无法识别SFINAE使第二版test()与第一版不同,需要一点提示;一个简单的虚拟参数就足够了,可以区分这两个版本。

#include <type_traits>
#include <iostream>
using std::enable_if_t; using std::cout;

template <typename T>
constexpr bool condition() { return sizeof(T) > 1; }

#ifdef    _MSC_VER
    #define MSVC_DUMMY int /*msvc_dummy*/ = 0
#else  // _MSC_VER
    #define MSVC_DUMMY
#endif // _MSC_VER

template <typename T>
enable_if_t<condition<T>()> test() { cout << "true\n"; }

template <typename T>
enable_if_t<!condition<T>()> test(MSVC_DUMMY) { cout << "false\n"; }

int main() {
    test<char>();
    test<int>();
}

这个works on MSVC,对代码的修改很少。一旦它们最终在没有提示的情况下工作,它也很容易被移除。

如果需要一致的界面,可以隐藏在辅助函数后面。

template <typename T>
enable_if_t<condition<T>()> test_() { cout << "true\n"; }

template <typename T>
enable_if_t<!condition<T>()> test_(MSVC_DUMMY) { cout << "false\n"; }

template <typename T>
auto test() { return test_<T>(); }