【问题标题】:Strange MSVC behaviour with std::experimental::is_detected带有 std::experimental::is_detected 的奇怪 MSVC 行为
【发布时间】:2016-02-26 20:28:46
【问题描述】:

我实现了基于on this article on cppreference.comstd::experimental::is_detected(部分代码在下面+工作重现)。

它在 G++ 和 Clang++ 上运行良好,但在使用 MSVC 时会导致奇怪的错误行为:is_detected 似乎总是bool_constant<true>

在这里您可以使用gcc 5.x 看到正确的结果: @ideone.com

但使用 MSVC 19(随 VS2015 提供)测试总是成功:

Z:\>cl /EHsc test.cxx
....
Z:\>test
true, true

那么,这是编译器中的一个已知错误吗?是否与未正确实施 Expression SFINAE 有关?我可以使用任何解决方法来完成这项工作吗?

谢谢!


这里是重现错误的部分代码(我省略了除了is_detected之外的其余界面以增加可读性):

#include <iostream>

// void_t: void type alias
template< typename... >
using void_t = void;
//

namespace internal
{
    // Fallback case
    template<   typename D,
                typename Void,
                template< typename... > class Check,
                typename... Args 
            >
    struct detect_impl
    {
        using value_t = std::false_type;
        using type = D;
    };


    // Check succeeded
    template<   typename D,
                template< typename... > class Check,
                typename... Args 
            >
    struct detect_impl
        < D, void_t< Check<Args...> >, Check, Args... >
    {
        using value_t = std::true_type;
        using type = Check<Args...>;
    };
}

// Type representing a missing type.
struct nonesuch
{
    nonesuch() = delete;
    ~nonesuch() = delete;
    nonesuch(nonesuch const&) = delete;
    void operator=(nonesuch const&) = delete;
};


template<   template< typename... > class Check,
            typename... Args
        >
using is_detected = typename internal::detect_impl< nonesuch, void, Check, Args... >::value_t;



// Our test
template< typename T >
using is_addable_impl = decltype( std::declval<T>() + std::declval<T>() );

template< typename T >
using is_addable = is_detected<is_addable_impl, T>;


auto main(int argc, const char* arv[])
    -> int
{
    std::cout   <<  std::boolalpha
                <<  is_addable<int>::value  << ", "
                <<  is_addable<nonesuch>::value << std::endl;
}

编辑:奇怪的是,直接使用 void_t 成语适用于 MSVC:

#include <iostream>
#include <type_traits>

struct X {};

template< typename T, typename = void >
struct is_addable
    : std::false_type
{};

 template< typename T >
 struct is_addable <T, std::void_t<decltype(std::declval<T>() + std::declval<T>())>>
     : std::true_type
 {};

int main()
{
    std::cout   <<  std::boolalpha 
                << is_addable<int>::value   << ", "
                << is_addable<X>::value
                << std::endl;
}

输出:

Z:\>cl /EHsc test.cxx
....
Z:\>test.exe
true, false

【问题讨论】:

  • 如果您正在尝试使用基本 VS2015 进行此操作,请至少尝试更新 1。添加了部分表达式 SFINAE 支持。
  • @chris 很遗憾,我已经在使用最新的 CTP 版本了。

标签: c++ c++14 sfinae c++17


【解决方案1】:

这是一个似乎适用于最近的 MSVC 的解决方法(使用 Visual C++ 19.00.23720.0 测试):

#include <type_traits>

template <typename...>
using void_t = void;

namespace internal
{
    template <typename V, typename D>
    struct detect_impl
    {
        using value_t = V;
        using type = D;
    };

    template <typename D, template <typename...> class Check, typename... Args>
    auto detect_check(char)
        -> detect_impl<std::false_type, D>;

    template <typename D, template <typename...> class Check, typename... Args>
    auto detect_check(int)
        -> decltype(void_t<Check<Args...>>(),
                    detect_impl<std::true_type, Check<Args...>>{});

    template <typename D, typename Void, template <typename...> class Check, typename... Args>
    struct detect : decltype(detect_check<D, Check, Args...>(0)) {};
}

struct nonesuch
{
    nonesuch() = delete;
    ~nonesuch() = delete;
    nonesuch(nonesuch const&) = delete;
    void operator=(nonesuch const&) = delete;
};

template <template< typename... > class Check, typename... Args>
using is_detected = typename internal::detect<nonesuch, void, Check, Args...>::value_t;

(虚拟的 void 参数现在没有使用,它只是为了保持实现的其余部分完好无损。)

【讨论】:

    【解决方案2】:

    上面的代码编译需要C++11编译器,MSVC 2015不是C++11编译器。

    您遇到的 C++11 合规性方面的特殊缺陷被微软称为“表达式 SFINAE”。请留意它是否已修复。

    基本上,decltype 不能用于 SFINAE。通俗地说,SFINAE 是您用来选择模板函数或类的重载的技术。

    通常没有解决方法。

    【讨论】:

    • 我会说它不是一个符合标准的编译器。它确实支持大多数 C++11/14 功能,
    • (看我的编辑)那为什么直接应用 void_t 成语是有效的,而使用detect_impl的间接方式却不行呢?
    • @Naschkatze 也许吧。他们的编译器在decltype 下的行为往往是不稳定和脆弱的(而不是“显然不工作”)。有时我可以让它工作,但除非访问它们的来源,我无法预测它。
    • @NathanOliver 你比我更慈善。这是允许的。
    • 公平地说,它并没有伪装成 C++11 编译器:__cplusplus == 199711L ;)
    猜你喜欢
    • 2020-10-17
    • 2022-01-08
    • 1970-01-01
    • 2023-03-14
    • 1970-01-01
    • 2011-03-06
    • 2020-06-11
    • 2013-03-05
    • 1970-01-01
    相关资源
    最近更新 更多