Home > Enterprise >  How do I write SFINAE for copy-list-init in a return statement, in a portable way?
How do I write SFINAE for copy-list-init in a return statement, in a portable way?

Time:08-12

I'm trying to make the following function SFINAE-friendly:

#include <utility>

template <typename T, typename ...P>
T foo(P &&... params)
{
    return {std::forward<P>(params)...};
}

I can't put a return statement in a SFINAE context, but since this is an example of copy-list-initialization, I figured I could find a different example that is SFINAE-able.

There is T({...}), but only GCC accepts it:

template <typename T, typename ...P>
T foo(P &&... params)
requires requires{T({std::forward<P>(params)...});}
{
    return {std::forward<P>(params)...};
}
#include <array>

struct A
{
    A(int) {}
    A(const A &) = delete;
    A &operator=(const A &) = delete;
};

int main()
{
    foo<std::array<A, 3>>(1, 2, 3); // Error: call to implicitly-deleted copy constructor of `std::array`.
}

There is also foo({...}) (a function call). GCC and Clang accept it, but MSVC rejects it:

template <typename T>
void accept(T) noexcept;

template <typename T, typename ...P>
T foo(P &&... params)
requires requires{accept<T>({std::forward<P>(params)...});}
{
    return {std::forward<P>(params)...};
}

How do I work around the compiler bugs, and write this SFINAE in a portable way?

CodePudding user response:

The second version works if you take const& parameter in accept:

template <typename T>
void accept(const T&) noexcept;

CodePudding user response:

If you remove extra parenthesis, it compiles for all compilers:

template <typename T, typename ...P>
T foo(P &&... params)
requires requires{T{std::forward<P>(params)...};}
{
    return {std::forward<P>(params)...};
}

Demo

  • Related