Why avoid friends, or how I lost all my advantages

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. — , , , , .







, . - 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.

, — , .







 — , ?








, !








All Articles