IT戦記

プログラミング、起業などについて書いているプログラマーのブログです😚

C++ 私的めも

クラステンプレート内で frinend 関数を定義した場合の名前の扱い

Every name first declared in a namespace is a member of that namespace. If a friend declaration in a non-local class first declares a class or function*1 the friend class or function is a member of the innermost enclosing namespace. The name of the friend is not found by simple name lookup until a matching declaration is provided in that namespace scope (either before or after the class declaration granting friendship). If a friend function is called, its name may be found by the name lookup that considers functions from namespaces and classes associated with the types of the function arguments (basic.lookup.koenig). When looking for a prior declaration of a class or a function declared as a friend, scopes outside the innermost enclosing namespace scope are not considered. [Example:

    //  Assume  f  and  g  have not yet been defined.
    void h(int);
    namespace A {
    	class X {
    		friend void f(X);       //   A::f  is a friend
    		class Y {
    			friend void g();        //   A::g  is a friend
    			friend void h(int);     //   A::h  is a friend
    						//   ::h  not considered
    		};
    	};

    	//   A::f,  A::g  and  A::h  are not visible here
    	X x;
    	void g() { f(x); }              //  definition of  A::g
    	void f(X) { /* ... */}          //  definition of  A::f
    	void h(int) { /* ... */ }       //  definition of  A::h
    	//   A::f,  A::g  and  A::h  are visible here and known to be friends
    }

    using A::x;

    void h()
    {
    	A::f(x);
    	A::X::f(x);             //  error:  f  is not a member of  A::X
    	A::X::Y::g();           //  error:  g  is not a member of  A::X::Y
    }
http://www.kuzbass.ru:8086/docs/isocpp/dcl.html

As with non-template classes, the names of namespace-scope friend functions of a class template specialization are not visible during an ordinary lookup unless explicitly declared at namespace scope (class.friend). Such names may be found under the rules for associated classes (basic.lookup.koenig).*2

    template<typename T> class number {
    	number(int);
    	//...
    	friend number gcd(number& x, number& y) { /* ... */ }
    	//...
    };

    void g()
    {
    	number<double> a(3), b(4);
    	//...
    	a = gcd(a,b);           //  finds  gcd  because  number<double>  is an
    				//  associated class, making  gcd  visible
    				//  in its namespace (global scope)
    	b = gcd(3,4);           //  ill-formed;  gcd  is not visible
    }
http://www.kuzbass.ru:8086/docs/isocpp/template.html

On some compilers (e.g. Borland, GCC) even single-inheritance seems to cause an increase in object size in some cases. If you are not defining a class template, you may get better object-size performance by avoiding derivation altogether, and instead explicitly instantiating the operator template as follows:

        class myclass // lose the inheritance...
        {
            //...
        };

        // explicitly instantiate the operators I need.
        template struct less_than_comparable<myclass>;
        template struct equality_comparable<myclass>;
        template struct incrementable<myclass>;
        template struct decrementable<myclass>;
        template struct addable<myclass,long>;
        template struct subtractable<myclass,long>;

Note that some operator templates cannot use this workaround and must be a base class of their primary operand type. Those templates define operators which must be member functions, and the workaround needs the operators to be independent friend functions. The relevant templates are:

  • dereferenceable<>
  • indexable<>
  • Any composite operator template that includes at least one of the above

As Daniel Krügler pointed out, this technique violates 14.6.5/2 and is thus non-portable. The reasoning is, that the operators injected by the instantiation of e.g. less_than_comparable can not be found by ADL according to the rules given by 3.4.2/2, since myclass is not an associated class of less_than_comparable. Thus only use this technique if all else fails.

Header <boost/operators.hpp> Documentation - 1.36.0

"associated class", "associated namespace" とは

  • 基本型
    • 両方なし
  • クラス型
    • associated class
      • 基底クラス(間接も含む)
    • associated namespace
  • union, enum
    • associated class
      • クラスメンバだったら、そのクラス
      • クラスメンバじゃなかったら無し
    • associated namespace
  • ポインタ、配列
    • ポインタが指すクラス、配列の要素となるクラスの associated class と associated namespace
  • 関数
    • 仮引数と返却値の型の associated class と associated namespace
  • メンバ関数のポインタ
    • 仮引数と返却値の型の associated class と associated namespace
    • メンバを持っているクラスの associated class と associated namespace
  • データメンバのポインタ
    • メンバの型とメンバを持っているクラスの associated class と associated namespace
  • テンプレート
    • テンプレートが定義されたクラスおよび名前空間
    • テンプレート引数として与えられた型(テンプレートは含まない)の asssociated class と associated namespace
    • テンプレート引数として与えられたテンプレートが定義された名前空間
    • テンプレート引数として与えられたテンプレートが定義されたクラス

For each argument type T in the function call, there is a set of zero or more associated namespaces and a set of zero or more associated classes to be considered. The sets of namespaces and classes is determined entirely by the types of the function arguments (and the namespace of any template template argument). Typedef names and using-declarations used to specify the types do not contribute to this set. The sets of namespaces and classes are determined in the following way:

  • If T is a fundamental type, its associated sets of namespaces and classes are both empty.
  • If T is a class type, its associated classes are the class itself and its direct and indirect base classes. Its associated namespaces are the namespaces in which its associated classes are defined.
  • If T is a union or enumeration type, its associated namespace is the namespace in which it is defined. If it is a class member, its associated class is the member's class; else it has no associated class.
  • If T is a pointer to U or an array of U, its associated namespaces and classes are those associated with U.
  • If T is a function type, its associated namespaces and classes are those associated with the function parameter types and those associated with the return type.
  • If T is a pointer to a member function of a class X, its associated namespaces and classes are those associated with the function parameter types and return type, together with those associated with X.
  • If T is a pointer to a data member of class X, its associated namespaces and classes are those associated with the member type together with those associated with X.
  • If T is a template-id, its associated namespaces and classes are the namespace in which the template is defined; for member templates, the member template's class; the namespaces and classes associated with the types of the template arguments provided for template type parameters (excluding template template parameters); the namespaces in which any template template arguments are defined; and the classes in which any member templates used as template template arguments are defined.(non-type template arguments do not contribute to the set of associated namespaces. )

If the ordinary unqualified lookup of the name finds the declaration of a class member function, the associated namespaces and classes are not considered. Otherwise the set of declarations found by the lookup of the function name is the union of the set of declarations found using ordinary unqualified lookup and the set of declarations found in the namespaces and classes associated with the argument types.

    namespace NS {
        class T { };
        void f(T);
    }
    NS::T parm;
    int main() {
        f(parm);                    //  OK: calls  NS::f
    }
http://www.kuzbass.ru:8086/docs/isocpp/basic.html

When considering an associated namespace, the lookup is the same as the lookup performed when the associated namespace is used as a qualifier (namespace.qual) except that:

  • Any using-directives in the associated namespace are ignored.
  • Any namespace-scope friend functions declared in associated classes are visible within their respective namespaces even if they are not visible during an ordinary lookup (class.friend).
http://www.kuzbass.ru:8086/docs/isocpp/basic.html

*1:this implies that the name of the class or function is unqualified.

*2:Friend declarations do not introduce new names into any scope, either when the template is declared or when it is instantiated.