C ++在模板函数中强制执行第二遍名称查找

时间:2015-01-28 07:52:09

标签: c++ templates name-lookup

是否有某种方法可以强制C ++编译器在模板实例化期间(而不是之前)对给定符号执行名称查找?

给出以下代码:

template <class T>
auto wrapper( T t ) -> decltype( f( t ) )
{
    return f( t );
}

unsigned char f( int x ) { return x % 256; }
unsigned char f( unsigned char x ) { return x; }

int main( int, char ** )
{
    auto x = wrapper( 3100 );
    return 0;
}

我能做些什么(除了将f的定义移到顶部之外),以便使代码编译并给出相同的结果,就像之前f的所有定义都可用一样wrapper的定义?

我找不到任何东西,可能是因为我不知道如何恰当地说出这个问题。如果有帮助,可以假定f的所有参数类型都是用户定义的类型。

3 个答案:

答案 0 :(得分:2)

  

是否有某种方法可以强制C ++编译器为a执行名称查找   在模板实例化期间(而不是之前)给出符号?

是。首先,名称必须依赖。 f用作wrapper时的名称f(t)是依赖的,因为t与类型有关。 [temp.dep] / 1:

  

表格形式:

     

后缀表达式   ( 表达式列表 opt )

     

postfix-expression unqualified-id ,    unqualified-id 表示依赖名称

     
      
  • 表达式列表中的任何表达式都是包扩展(14.5.3),
  •   
  • 表达式列表中的任何表达式都是依赖于类型的表达式(14.6.2.2),或
  •   
  • 如果 unqualified-id template-id ,其中任何模板参数都取决于模板参数。
  •   

问题是在模板本身之后声明的名称,即仅在实例化而不是定义上下文中,可以使用参数依赖名称查找单独找到。您的f重载只接受基本类型,但根据[basic.lookup.argdep] / 2,它们没有与它们关联的全局命名空间:

  

如果T是基本类型,则其关联的命名空间集合和   课程都是空的。

因此,如果参数与参数的类型相同,则永远无法找到您声明的f。一个小技巧可以帮助:

template <typename T>
struct refwrap
{
    T&& t;
    refwrap(T&& t) : t(std::forward<T>(t)) {}
    operator T&&() {return std::forward<T>(t);}
};

template <typename T>
auto make_refwrap( T&& t ) -> refwrap<T> // making use of reference collapsing
{ return {std::forward<T>(t)}; }         // inside refwrap to get forwarding

此模板在全局命名空间中声明时,将导致ADL考虑它。重写wrapper如下:

template <class T>
auto wrapper( T t ) -> decltype( f( make_refwrap(t) ) )
{
    return f( make_refwrap(t) );
}

Demo。这不是正确的方法,因为它会在更复杂的情况下失败。

答案 1 :(得分:1)

这适用于模板专业化。请注意,您必须确定默认函数是什么,因为我无法看到它的问题。

// default function
template <class T>
unsigned char f( T x ) { return x; }

// specialization for int
template <>
unsigned char f( int x ) { return x % 256; }

int main( int, char ** )
{
    auto x = f( 3100 );
    return 0;
}

答案 2 :(得分:0)

以下代码不是很干净,但说明了如何使用类模板特化来解决问题。它维护原始界面(即fwrapper可以像以前一样使用。)

感谢您给我正确的提示。我愿意接受一个不那么冗长的解决方案。

#include <type_traits>

template <class ...>
struct F;

template <class T>
auto wrapper( T t )
    -> decltype( F<typename std::decay<T>::type>::f( t ) )
{
    return F<typename std::decay<T>::type>::f( t );
}

template <>
struct F<unsigned char>
{
    static unsigned char f( unsigned char x ) { return x; }
};

template <>
struct F<int>
{
    static unsigned char f( int x ) { return x % 256; }
};

template <class T>
auto f( T t )
    -> decltype( F<typename std::decay<T>::type>::f( t ) )
{
    return F<typename std::decay<T>::type>::f( t );
}

int main( int, char ** )
{
    auto x = wrapper( 3100 );
    return 0;
}