SFINAE and noexcept specifier

2019-04-06 09:32发布

Does an expression in noexcept specifier's parentheses participate in SFINAE during overload resolution of function templates?

I want to make an wrapper for aggregates and want the std::is_constructible predicate to work properly for it:

template< typename type >
struct embrace
    : type
{

    template< typename ...arguments >
    embrace(arguments &&... _arguments) noexcept(noexcept(type{std::forward< arguments >(_arguments)...}))
        : type{std::forward< arguments >(_arguments)...} // braces 
    { ; }

};

int
main()
{
    struct S { int i; double j; }; // aggregate
    using E = embrace< S >;
    E b(1, 1.0); // "parentheses"-constructible => can be used as usual types
    b.i = 1; b.j = 2.0; // accessible
    static_assert(std::is_constructible< E, int, double >{});
    static_assert(std::is_constructible< E, struct B >{}); // want hard error here
    return EXIT_SUCCESS;
}

But my attempt to use noexcept operator inside noexcept specification to enable SFINAE is failed, and the templated constructor accepts everything passed to it. How can the constructor be restricted?

It is not permitted by the Standard to specialize any predicates from <type_traits>. How to deal with c-tors that accepts variadic template parameter packs and SFINAE in general? Is there an impasse and inherent language flaw?

3条回答
干净又极端
2楼-- · 2019-04-06 09:34

Does an expression in noexcept specifier's parentheses participate in SFINAE during overload resolution of function templates?

It doesn't participate in template deduction because the noexcept specifier is not part of a function's type.

The noexcept-specification is not a part of the function type. (until C++17)

Source

Therefore, when your template parameter type is deduced, noexcept is not part of the deduced type. Your compiler seems to return true for any type which is why you aren't able to detect whether it is noexcept or not; that is why everything is accepted.

I ran into the same issue. You can check my question/answer here:

How can I detect whether a template argument is a noexcept function?

Basically, your only option is to wait for a C++17 compliant compiler.

查看更多
仙女界的扛把子
3楼-- · 2019-04-06 09:55

SFINAE simply doesn't apply to exception-specifications, whether or not noexcept is part of the function type.

See the note in [temp.deduct]/7:

The substitution occurs in all types and expressions that are used in the function type and in template parameter declarations. The expressions include not only constant expressions such as those that appear in array bounds or as nontype template arguments but also general expressions (i.e., non-constant expressions) inside sizeof, decltype, and other contexts that allow non-constant expressions. The substitution proceeds in lexical order and stops when a condition that causes deduction to fail is encountered. [ Note: The equivalent substitution in exception specifications is done only when the exception-specification is instantiated, at which point a program is ill-formed if the substitution results in an invalid type or expression. —end note ]

P0012R1 didn't change anything in this respect.

Piotr's answer covers the fix for your code.

查看更多
Anthone
4楼-- · 2019-04-06 09:57

How can the constructor be restricted?

#include <utility>

template <typename type>
struct embrace : type
{
    template <typename... arguments
            , typename = decltype(type{std::declval<arguments>()...})>
    embrace(arguments&&... _arguments)
        noexcept(noexcept(type{std::forward<arguments>(_arguments)...}))
        : type{std::forward<arguments>(_arguments)...}
    {
    }
};

DEMO

(or shorter):

#include <utility>

template <typename type>
struct embrace : type
{
    template <typename... arguments
            , bool NoExcept = noexcept(type{std::declval<arguments>()...})>
    constexpr
    embrace(arguments&&... _arguments)
        noexcept(NoExcept)
        : type{std::forward<arguments>(_arguments)...}
    {
    }
};

DEMO 2

查看更多
登录 后发表回答