Hallo Habr.
Vor ein paar Tagen bin ich auf diesen Tweet gestoßen:
Kurz gesagt: Wieder einmal fanden sie in C ++ eine Art Mist, der dort selbst auftauchte, emergent-konvergent, wie Schleim aus einer kurzen Science-Fiction-Geschichte, die ich in meiner Kindheit gelesen hatte und die versehentlich in städtischen Abwasserkanälen entstand und zu einem universellen organischen Lösungsmittel heranwuchs.
Lassen Sie es uns herausfinden, da es nicht lange dauern wird (gemäß dem Text des Standards bin ich nicht länger als ein paar Stunden gesprungen). Und Spaß, Links zu Standard machen immer Spaß.
Hier ist der gesamte 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. — , , , , .
, . - C++14, C++11, 03.
? :
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.
, — , .
, !