Concept vs. Typename Parameter in Template Template

70 Views Asked by At

I was curious if this is defined behavior in C++23 to have a template-template that takes a typename, and be able to insert a template which accepts a concept (which accepts a typename)?

Here is an example:

#include <iostream>
#include <type_traits>

template<typename T>
concept MyConcept = std::is_integral_v<T> || std::is_floating_point_v<T>;

template<template<typename> typename TT>
struct A {
    template<typename T>
    using Type = TT<T>;
};


template<MyConcept T>
struct B {
    T bb;
};

int main(int argc, char** args)
{
    auto a = A<B>{}; // Ok? Compiles with gcc 13.2.1 and clang-16.0.6
    auto b = decltype(a)::Type<int>{};
    return 0;
}

The converse also seems to compile which makes me more suspicious


template<template<MyConcept> typename TT>
struct A {
    template<typename T>
    using Type = TT<T>;
};


template<typename T>
struct B {
    T bb;
};

int main(int argc, char** args)
{
    auto a = A<B>{}; // Ok? Compiles with gcc 13.2.1 and clang-16.0.6
    auto b = decltype(a)::Type<int>{};
    return 0;
}

Here, under a certain reading, we should accept a template into the template-template iff that template accepts a MyConcept, but B's template is unconstrained?

Are either of these examples undefined behavior, or are they behaving as the standard defines?

Thank you!

1

There are 1 best solutions below

0
The Floating Brain On BEST ANSWER

According to the publicly available working draft of the C++23 standard it seems like this is defined behavior and is correct. Example 4 under § 13.4.4 paragraph 3 seems to show exactly this, from the standard:

template<typename T> concept C = requires (T t) { t.f(); };
template<typename T> concept D = C<T> && requires (T t) { t.g(); };
template<template<C> class P> struct S { };
template<C> struct X { };
template<D> struct Y { };
template<typename T> struct Z { };

S<X> s1; // OK, X and P have equivalent constraints
S<Y> s2; // error: P is not at least as specialized as Y
S<Z> s3; // OK, P is at least as specialized as Z

Also this from example 2 looked relevant:

template<auto n> class D { /* ... */ };
//...
template<template<int> class R> class Z { /* ... */ };
//...
Z<D> zd; // OK