Salut, Habr.
Il y a quelques jours, je suis tombé sur ce tweet:
En bref: une fois de plus, en C ++, ils ont trouvé une sorte de merde qui y apparaissait elle-même, émergente-convergente, comme la boue d'une courte histoire de science-fiction que j'ai lue dans mon enfance, qui a surgi accidentellement dans les égouts urbains et est devenue un solvant organique universel.
Voyons cela, car ce ne sera pas long (selon le texte de la norme, je n'ai pas sauté plus de quelques heures). Et amusant, les liens vers Standard sont toujours amusants.
Voici tout le 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.
, — , .
, !