【问题标题】:Type inference for `T::some_typredef` in Templated Function模板化函数中 `T::some_typredef` 的类型推断
【发布时间】:2011-07-06 17:46:58
【问题描述】:

给定一个模板函数,它不直接使用模板参数进行输入。 C++ 类型推断如何工作?例如给定

template<typename T>
void f(T::value_type){}

这个函数什么时候(如果有的话)类型推理工作?

除了template&lt;typename T1,...&gt;void f(T1,T2,...),还有其他地方可能发生类型推断吗?

与往常一样,引用额外学分的标准。

【问题讨论】:

    标签: c++ templates


    【解决方案1】:

    它永远不会对嵌套类型起作用,除非 T 也是一个参数。

    如果您调用f(1),编译器将没有机会找到所有带有嵌套typedef int value_type; 的T。

    您可以推断出属于参数类型的类型,例如

    template<class T>
    void f(std::vector<T>);
    

    【讨论】:

      【解决方案2】:

      我想这是你的答案:

      14.8.2.4 - 从类型 [temp.deduct.type] 推导出模板参数

      [...]

      -3- [...]

      在大多数情况下,用于组成P 的类型、模板和非类型值参与模板参数推导。也就是说,它们可用于确定模板参数的值,并且如此确定的值必须与其他地方确定的值一致。然而,在某些情况下,该值不参与类型推导,而是使用在其他地方推导或明确指定的模板参数的值。如果模板形参仅在非推导上下文中使用且未显式指定,则模板实参推导失败。

      -4- 未推断的上下文是:

      • 使用qualified-id指定的类型的nested-name-specifier

      • 一种 template-id 类型,其中一个或多个 template-arguments 是引用 template-parameter的表达式>.

      当以包含非推导上下文的方式指定类型名称时,构成该类型名称的所有类型也都是非推导的。但是,复合类型可以包括推导类型和非推导类型。 [示例:如果将类型指定为A&lt;T&gt;::B&lt;T2&gt;,则TT2 都是不可推导的。同样,如果将类型指定为 A&lt;I+J&gt;::X&lt;T&gt;IJT,则为非推导。如果类型指定为void f(A&lt;T&gt;::B, A&lt;T&gt;),则A&lt;T&gt;::B 中的T 是非推导的,但A&lt;T&gt; 中的T 是推导的。 ]

      您的T::value_type 是一个类型的qualified-id,因此其nested-name-specifier 中的类型是非推导的,必须明确指定。

      编辑:此信息来自ISO/IEC 14882:1998

      【讨论】:

      • 标准中的准确报价 - 获得金丝带。显然在template&lt;typename T&gt; 中只有T 被扣除。我真的不知道为什么最佳答案得到了这么多选票。
      • @Elazar,从常识的角度来看,其他答案是正确的。此外,我确信同样的常识是我引用的规范文章的基础。我花了一些时间来查找和格式化报价,因此我获得投票的时间更少。
      • 我的常识也说这是不可能的,但常识和C++ 并不总是合二为一 ;-) 5 分钟后的好答案比 1 分钟后的平庸答案获得的票数更少,这真是太可惜了分钟。
      【解决方案3】:

      标准要求您消除依赖类型的歧义:

      template<typename T>
      void f(typename T::value_type){}
      

      过去,该领域存在一些不太标准的行为,导致代码在一个 (MSVC) 编译器上编译,但在另一个 (GCC) 编译器上编译不上。这些天来,可能是受到 Boost 等高度通用的标准库的影响,编译器似乎只接受正确的代码。


      disambiguate identifiers dependent on template arguments(也称为this)有时需要 typename 关键字。可以这样想:在解析模板定义时,您必须为编译器提供足够的信息来完成第一次语法检查。当时还不知道实际的模板参数,并且(C++ 涉及语法)您必须向编译器提示稍后令牌将代表哪种符号

      【讨论】:

        【解决方案4】:

        除非您以某种方式指定T 参数,否则编译器不会尝试以任何方式扣除T

        即使您明确指定它,我相信它只会在提供原始定义的情况下工作,而不是通过 typedefs。

        考虑以下示例(使用 clang++ 编译,显然 g++ 失败):

        #include <stdio.h>
        template <typename T>
        void foo(T) {
        printf("foo(T)\n");
        }
        
        template <typename T>
        void foo(typename T::value) {
        printf("foo(T::value)\n");
        }
        
        struct X {
          class value {};
        };
        
        struct Z {
          typedef int value;
        };
        
        struct XZ {
          typedef Z value;
        };
        
        typedef X::value Xv;
        
        #define CALL(function,param) printf(#function " (" #param ") = "); function(param());
        
        void explicitCalls() {
          printf("Explicit calls:\n");
          CALL(foo<int>,int);
          CALL(foo<X::value>,X::value);
          CALL(foo<Z::value>,Z::value);
          CALL(foo<XZ::value>,XZ::value);
          CALL(foo<Xv>,Xv);
        }
        
        void implicitCalls() {
          printf("Implicit calls:\n");
          CALL(foo,int);
          CALL(foo,X::value);
          CALL(foo,Z::value);
          CALL(foo,XZ::value);
          CALL(foo,Xv);
        }
        
        int main() {
          explicitCalls();
          implicitCalls();
        }
        

        输出是:

        Explicit calls:
        foo<int> (int) = foo(T)
        foo<X::value> (X::value) = foo(T::value)
        foo<Z::value> (Z::value) = foo(T)
        foo<XZ::value> (XZ::value) = foo(T)
        foo<Xv> (Xv) = foo(T::value)
        Implicit calls:
        foo (int) = foo(T)
        foo (X::value) = foo(T)
        foo (Z::value) = foo(T)
        foo (XZ::value) = foo(T)
        foo (Xv) = foo(T)
        

        【讨论】:

          猜你喜欢
          • 2012-04-18
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2013-08-25
          • 2017-08-06
          • 2019-09-12
          • 1970-01-01
          相关资源
          最近更新 更多