Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/templates/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ 模板内与参数相关的查找_C++_Templates_Argument Dependent Lookup - Fatal编程技术网

C++ 模板内与参数相关的查找

C++ 模板内与参数相关的查找,c++,templates,argument-dependent-lookup,C++,Templates,Argument Dependent Lookup,我对模板上下文中的函数名查找感到困惑。我知道编译器会延迟模板化代码中与参数相关的标识符查找,直到模板被实例化。这意味着您有时会出现语法错误或调用模板化代码中不存在的函数,除非您实际实例化模板,否则编译器不会抱怨 然而,我发现不同编译器之间存在差异,我想知道标准本身需要什么 考虑以下代码: #include <iostream> class Foo { public: template <class T> void bar(T v) {

我对模板上下文中的函数名查找感到困惑。我知道编译器会延迟模板化代码中与参数相关的标识符查找,直到模板被实例化。这意味着您有时会出现语法错误或调用模板化代码中不存在的函数,除非您实际实例化模板,否则编译器不会抱怨

然而,我发现不同编译器之间存在差异,我想知道标准本身需要什么

考虑以下代码:

#include <iostream>

class Foo
{
    public:

    template <class T>
    void bar(T v)
    {
        do_something(v);
    }
};

void do_something(std::string s)
{
    std::cout << "do_something(std::string)" << std::endl;
}

void do_something(int x)
{
    std::cout << "do_something(int)" << std::endl;
}

int main()
{
    Foo f;
    f.bar("abc");
    f.bar(123);
}

因此,编译器似乎将标识符查找延迟到模板实例化之后,此时它能够找到
do\u something

相反,GCC 4.7.2将编译上述程序。它会产生以下错误:

test.cc: In instantiation of ‘void Foo::bar(T) [with T = const char*]’:
test.cc:27:13:   required from here
test.cc:10:3: error: ‘do_something’ was not declared in this scope, and no declarations were found by argument-dependent lookup at the point of instantiation [-fpermissive]
test.cc:19:6: note: ‘void do_something(int)’ declared here, later in the translation unit
因此,GCC4.7.2知道,
do_something
后来被声明,但拒绝编译程序,因为
do_something
不依赖于参数

所以,我假设GCC4.7.2在这里可能是正确的,而GCC4.6.3是错误的。因此,我可能需要在定义
Foo::bar
之前声明
do\u something
。问题是,假设我想允许我的类
Foo
的用户通过实现自己的
do\u something
重载来扩展
Foo::bar
的行为。我需要写一些东西,比如:

#include <iostream>

template <class T>
void do_something(T v)
{
    std::cout << "do_something(T)" << std::endl;
}

class Foo
{
    public:

    template <class T>
    void bar(T v)
    {
        do_something(v);
    }
};

void do_something(int x)
{
    std::cout << "do_something(int)" << std::endl;
}

int main()
{
    Foo f;
    f.bar("abc");
    f.bar(123);
}

那么这里有什么解决方案呢?如何允许用户通过实现自己的
do\u something
重载来扩展
Foo::bar

至于重载
do\u something
,您需要专门化原始模板:

template<>
void do_something<int>(int x) {
    std::cout << "do_something(int)" << std::endl;
}
模板
void do_something(int x){

std::这在这里行得通,但总的来说这是个糟糕的主意。名称查找和模板专门化以非常奇怪的方式相互作用。@MatthieuM。想详细说明一下吗?就我所理解的模板专门化而言(我承认这可能不是一个完全的理解)专业化是在实例化点选择的。我意识到这可能过于简化了,但我从来没有被它咬过(即使我经常使用它),所以我从来没有深入挖掘过。;(Herb Sutters最好地说明了这一点。重点是,首先是“真实的”查找函数模板,选择最佳匹配,只有当有要考虑的基函数模板的专业化时,编译器才会查找。因此,要知道是否选择了专业化,您需要知道它与什么样的基函数模板有关……这是怪异的、真的、真的、奇怪的。谢谢,我现在明白了,这确实令人困惑(我也明白为什么我从来没有遇到过任何问题:我总是使用包装在结构中的静态函数来允许部分专门化——有趣的是,这正是H.Sutter在他的道德#2点中建议的)。我将相应地编辑我的答案。甚至引入“一刀切”对于最不理想的转换,问题仍然存在:
do_something(T)
do_something(T)
template<>
void do_something<int>(int x) {
    std::cout << "do_something(int)" << std::endl;
}
template<typename T>
struct DoSomething {
    static void do_something(T v) {
        std::cout << "do_something(T)" << std::endl;
    }
};

struct Foo
{
    template <class T>
    void bar(T v) {
        DoSomething<T>::do_something(v);
    }
};

// Now you can specialize safely
template<>
struct DoSomething<int> {
    static void do_something(int v) {
        std::cout << "do_something(int)" << std::endl;
    }
};