Search code examples
c++c++17ctad

User defined-deduction guide for nested template-types


Q1 : Are user-defined deduction guides allowed at namespace scope ?

In the example here, GCC and Clang does not produce the same behavior :

#include <tuple>

template <typename T>
struct some_type;
template <template <typename...> typename T, typename ... Ts>
struct some_type<T<Ts...>>
{
    template <typename U>
    class nested
    {
        U member;
    public:
        nested(U &&){}
    };

    // non-namespace scope user-deduction-guide : OK with Clang, fix the deduction issue
    template <typename U>
    nested(U&&) -> nested<U>;
};

void func()
{
    using pack_type = std::tuple<int, char>;
    some_type<pack_type>::nested{
        [](auto &&){}
    };
}

In short, we have a template-parametered type, with a nested type which is itself template-parametered, and template parameters have no relationship between each others.

template <typename T>
struct some_type;
template <template <typename...> typename T, typename ... Ts>
struct some_type<T<Ts...>>
{
    template <typename U>
    class nested // <- nested type, where `U` as no relationship with `T<Ts...>`
    {
        U member;
    public:
        nested(U &&);
    };
};

The standard specify : http://eel.is/c++draft/temp.deduct.guide#3

[...] A deduction-guide shall be declared in the same scope as the corresponding class template and, for a member class template, with the same access. [...]

Q2 : If no to Q1, what is the syntax to create a user-defined deduction guide for nested type, when there is no relationship between namespace-type and nested-type template-parameters ?

I'd expect a syntax close to :

template <template <typename...> typename T, typename ... Ts>
template <typename U>
some_type<T<Ts...>>::nested<U>::nested(U&&) -> nested<U>;

However, nested<U> is wrong, as it required a deduced type ... to deduce it.

Also, this is interpreted as a function with trailing return type void.

template <template <typename...> typename T, typename ... Ts>
template <typename U>
typename some_type<T<Ts...>>::template nested<U>::nested(U&&) -> nested<U>;

Thanks for your time.


Solution

  • Quick-fix

    The only work-around I found is to add a user-defined deduction guide only for Clang,
    which is way sub-optimal in term of maintainability.

    Live example on godbolt,
    or see the sources below.

    Why ?

    • GCC does not allow deduction guide in non-namespace context, Clang does.
    • Clang requires a deduction guide in this context, GCC does not

    NB : As posting this, clang trunk is 11.0.1 and gcc trunk is 10.2

    #include <tuple>
    
    template <typename T>
    struct type
    {
        template <typename U>
        struct nested
        {
            template <typename ... nested_Ts>
            nested(U &&, std::tuple<nested_Ts...> &&)
            {}
    
        };
        #if __clang__
        // here, user-defined deduction guide only for Clang
        template <typename U, typename ... Ts>
        nested(U&&, std::tuple<Ts...>&&) -> nested<U>;
        #endif
    };
    
    void instanciate_symbols()
    {
        using type = type<int>;
        [[maybe_unused]] auto value = type::nested{'a', std::tuple{42, .42f}};
    }