Splice specifiers (since C++26)
Produces a language construct from a reflection value, effectively "splicing" the reflection into source code.
Syntax
[: constant-expression :]
|
(1) | ||||||||
typename [: constant-expression :]
|
(2) | ||||||||
template [: constant-expression :]
|
(3) | ||||||||
| constant-expression | - | a converted constant expression of type std::meta::info |
<>), and must be the left operand of the scope resolution operator ::, e.g. template[:R:]<int>::is_always_lock_free.Explanation
In general, a splice specifier has the same meaning as the name or value of the language construct represented by constant-expression. It is said to designate that language construct.
Expression
For a splice specifier to be an expression, the specifier must not follow the typename keyword, nor be followed by the scope resolution operator ::.
struct S { static constexpr int a = 1; };
template<typename> struct TCls { static constexpr int b = 2; };
constexpr int c = [:^^S:]::a; // [:^^S:] is not an expression
constexpr int d = template [:^^TCls:]<int>::b; // template [:^^TCls:]<int> is not an expression
template<auto V> constexpr int e = [:V:]; // [:V:] is an expression
constexpr int f = template [:^^e:]<^^S::a>; // template [:^^e:]<^^S::a> is an expression
constexpr auto g = typename [:^^int:](42); // typename [:^^int:] is a type specifier
An unparenthesized splice expression cannot appear as a template argument.
A splice expression can designates a function, an object, a data member, a variable, a structured binding, a value, or an enumerator. But it cannot designate a constructor, a destructor, an unnamed bit-field, or a local entity such that naming it would cause it to be captured by a lambda expression.
A splice expression that designates a variable or structured binding V is valid only if V has static or thread storage duration or is declared in a scope enclosing the expression.
A splice expression that designates a non-static data member or implicit object member function can only be used:
- as the second operand of class member access expression;
- as the operand of address-of operator to form a pointer to member;
- in an unevaluated operand if it designates a non-static data member.
Unlike names of non-static members, a splice expression [:r:] is never implicitly transformed to this->[:r:].
A splice expression [:r:] may describe a direct base class relationship (D, B) (e.g. if r is an element of std::meta::bases_of(^^D, ctx)). A direct base class relationship can only appear as the second operand of class member access expression. This class member access expression converts the first operand to “reference to (possibly cv-qualified) D” and results in the B direct base class subobject of the converted value. This can be used to access a base class subject that would otherwise be ambiguous or inaccessible.
A splice expression can designate a function template or variable template only if the template keyword is used.
Type
A splice specifier that is a type specifier must not be followed by the scope resolution operator ::, and must either be preceded by typename, or be in a type-only context.
template<std::meta::info R> void tfn() {
typename [:R:]::type m; // OK, typename applies to the qualified name
// (Here [:R:] is a scope, not a type specifier.)
}
struct S { using type = int; };
void fn() {
[:^^S::type:] *var; // error: [:^^S::type:] is an expression
typename [:^^S::type:] *var; // OK, declares variable with type int*
}
using alias = [:^^S::type:]; // OK, type-only context
A splice type specifier can designate a type, a class template, or an alias template. If it designates a template and does not have a template argument list, it is subject to class template argument deduction.
A splice specifier may also appear in a type requirement.
template<typename T> concept C = requires {
typename [:T::r1:]; // fails if T::r1 is not a reflection of a type
typename [:T::r2:]<int>; // fails if T::r2 is not a reflection of some template Z
// for which Z<int> is a type
};
Scope
For a splice specifier that is the left operand of the the scope resolution operator ::, if the specifier has a template argument list, it must be preceded by typename or template.
template<int V>
struct TCls {
static constexpr int s = V;
using type = int;
};
int v1 = [:^^TCls<1>:]::s;
int v2 = template [:^^TCls:]<2>::s; // OK, 'template' is part of the splice specifier
typename [:^^TCls:]<3>::type v3 = 3; // OK, 'typename' is part of the typename-specifier
template [:^^TCls:]<3>::type v4 = 4; // OK, 'template' is part of the splice specifier
typename template [:^^TCls:]<3>::type v5 = 5; // OK, same as v3
[:^^TCls:]<3>::type v6 = 6; // error: unexpected <
The splice specifier must designate a class or enumeration type, or a namespace. The splice specifier in [:r:]<TArgs...>:: or template[:r:]<TArgs...>:: must designate a class template or alias template.
Namespace
A splice specifier [:r:] in a namespace alias definition or a using directive must designate a namespace name or namespace alias that is not the global namespace. A dependent r can appear in a namespace alias definition (but not in a using directive), which causes the namespace alias to be dependent as well.
Example
| This section is incomplete Reason: no example |