Hello, Habr.
A couple of days ago I came across this tweet:
In short: once again in C ++ they found some kind of crap that appeared there itself, emergent-convergent, like slime from one short science fiction story I read in my childhood, which accidentally arose in urban sewers and grew into a universal organic solvent.
Let's figure it out, since it will not be long (according to the text of the Standard I jumped no more than a couple of hours). And fun, links to Standard are always fun.
Here is the whole code:
#include <cstdio> class tag; template<class> struct type { friend constexpr auto get(type); }; template<class TKey, class TValue> struct set { friend constexpr auto get(TKey) { return TValue{}; } }; void foo() { // never called if constexpr(false) { // never true if (false) { // never true constexpr auto call = [](auto value) { std::printf("called %d", value); }; void(set<type<tag>, decltype(call)>{}); } } } int main() { get(type<tag>{})(42); // prints called 42 }
.
class tag;
, .
template<class> struct type { friend constexpr auto get(type); };
type
. , get
- .
, (13.9.1/1) type<T>
T
? ( , argument-dependent lookup, !) get(T)
(9.8.1.2/3, 13.9.1/4), (6.2/2.1).
template<class TKey, class TValue> struct set { friend constexpr auto get(TKey) { return TValue{}; } };
set
. , , get
- .
, set<K, V>
K
, V
? get(K)
, (6.2/2).
void foo() { if constexpr(false) { if (false) { constexpr auto call = [](auto value) { std::printf("called %d", value); }; void(set<type<tag>, decltype(call)>{}); } } }
, if (false)
, , :
void foo() { if constexpr(false) { constexpr auto call = [](auto value) { std::printf("called %d", value); }; set<type<tag>, decltype(call)>{}; } }
, if constexpr
, . ?
if constexpr
: 8.5.1/2. :
If the value of the converted condition is false, the first substatement is a discarded statement
, call
set
— discarded statement. .
:
During the instantiation of an enclosing templated entity, if the condition is not value-dependent after its instantiation, the discarded substatement (if any) is not instantiated.
discarded statement, , . false
, , value-dependent, «». «enclosing template entity», enclosing entity — foo
, . , , , if constexpr
.
. , type<tag>
, get(type<tag>)
, ADL, get
type<tag>
( 9.8.1.2/3). set<type<tag>, decltype(call)>
, get(type<tag>)
, type<tag>
. decltype(call)
, call
, C++20 (7.5.5.1/13), . main
get(type<tag>{})
, get
ADL. , call
, , 42.
.
, — discarded statement enclosing template entity. , void foo()
template<typename> void foo()
,
#include <cstdio> class tag; template<class> struct type { friend constexpr auto get(type); }; template<class TKey, class TValue> struct set { friend constexpr auto get(TKey) { return TValue{}; } }; template<typename> void foo() { if constexpr(false) { // never true if (false) { // never true constexpr auto call = [](auto value) { std::printf("called %d", value); }; void(set<type<tag>, decltype(call)>{}); } } } int main() { foo<int>(); get(type<tag>{})(42); // prints called 42 }
:
prog.cc:23:3: error: function 'get' with deduced return type cannot be used before it is defined get(type<tag>{})(42); // prints called 42 ^ prog.cc:6:37: note: 'get' declared here struct type { friend constexpr auto get(type); }; ^
, C++ - — , ( -), ( SFINAE, detector idiom ). , - C++?
, , C++. , — , - . — - , , - - template , . , , chaotic evil. — , , , , .
Defining a friend function in a template, then referencing that function later provides a means of capturing and retrieving metaprogramming state. This technique is arcane and should be made ill-formed.
Notes from the May, 2015 meeting:
CWG agreed that such techniques should be ill-formed, although the mechanism for prohibiting them is as yet undetermined.
, — , .
, !