Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

User defined-deduction guide for nested template-types

Tags:

c++

c++17

ctad

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

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

  • https://godbolt.org/z/8W6hznEjo
#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.

like image 268
Guss Avatar asked Mar 23 '21 14:03

Guss


1 Answers

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}};
}
like image 132
Guss Avatar answered Oct 19 '22 07:10

Guss