8

是否有某种方法可以强制 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如果有帮助,可以假定所有参数类型都是用户定义的类型。

4

3 回答 3

2

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

是的。首先,名称必须是依赖的。使用 as 时的名称f是依赖的,因为它依赖于类型。[温度.dep]/1:wrapperf(t)t

在以下形式的表达式中:

        后缀表达式 ( 表达式列表 选项 )

其中postfix-expressionunqualified-idunqualified-id表示从属名称,如果

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

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

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

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

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) );
}

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

于 2015-01-28T09:11:31.233 回答
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;
}
于 2015-01-28T08:08:33.843 回答
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;
}
于 2015-01-28T08:24:42.753 回答