簡體   English   中英

GCC中的模板沒有看到在其之后但在實例化之前聲明的函數

[英]Template in GCC doesn't see a function declared after it, but before instantiation

這是Visual Studio編譯良好的東西:

#include <string>
#include <vector>

template <typename T>
T read()
{
    T value;
    read(value); // gcc compile error here
    return value;
}

void read(std::string& str)
{
}

template <typename T>
void read(std::vector<T>& vec)
{
}

int main(int argc, char** argv) {

    read<std::string>();
    read<std::vector<int>>();
    read<std::vector<float>>();

    return 0;
}

但是GCC會產生編譯錯誤:沒有匹配的調用函數...

我找到了唯一適合我的解決方案,即在read()之前聲明模板函數read(T&):

template <typename T>
void read(T&);

template <typename T>
T read()
{
    T value;
    read(value);
    return value;
}

template <>
void read(std::string& str)
{
}

//template <typename T>
//void read(std::vector<T>& vec)
//{
//}

但我不知道如何在其中適合模板功能。

有什么更好的方法可以使gcc看到在使用它們的模板之后但在模板實例化之前聲明的函數?

考慮一下聲明的順序:

template <typename T>
T read()
{
    T value;
    read(value); // gcc compile error here
    return value;
}

void read(std::string& str) {}

template <typename T>
void read(std::vector<T>& vec) {}

GCC抱怨是因為依賴於參數的查找是唯一可以找到在實例化中聲明的名稱但不能在定義上下文中聲明的名稱的查找 ,卻找不到read的最后兩個重載。
這是因為

  1. std::string (或分別為std::vector<int>std::vector<float> )的關聯命名空間集包括std ,但不包括全局命名空間,在全局命名空間中聲明了重載。
  2. 任何基本類型(包括intfloat的關聯名稱空間的集合為空。

因此,永遠不會在全局名稱空間中搜索模板后聲明的重載。 實例化上下文中名稱的這種延遲查找顯然是在VC ++中進行的,這就是為什么您的代碼可以使用它進行編譯的原因。

通過聲明ADL在定義上下文中未找到的重載來解決此問題,以便可以通過普通的非限定名稱查找來找到它們。

void read(std::string& str);
template <typename T>
void read(std::vector<T>& vec);

template <typename T>
T read()
{
    T value;
    read(value); // gcc compile error here
    return value;
}

使您的代碼編譯 定義顯然也是聲明,因此將整個定義放在那里也是可行的(如果后兩個重載的定義不依賴於該函數模板!)。

為了允許以后的覆蓋(不進行轉發),您可以引入一個標簽(在聲明讀取的名稱空間中)以查找名稱:

#include <string>
#include <vector>

namespace Read {
    template <typename T>
    struct Tag {};

    template <typename T>
    T read()
    {
        T value;
        read(Tag<T>(), value);
        return value;
    }
} // namespace Read

template <typename T>
T read()
{
    return Read::read<T>();
}

namespace Read {
    void read(Tag<std::string>, std::string& str)
    {
    }

    template <typename T>
    void read(Tag<std::vector<T>>, std::vector<T>& vec)
    {
    }
} // namespace Read

int main(int argc, char** argv) {

    read<std::string>();
    read<std::vector<int>>();
    read<std::vector<float>>();

    return 0;
}

(注意:它也可以在全局名稱空間中使用)

您不只是交換訂單。 您更改了此:

template <typename T>
void read(std::vector<T>& vec)
{}

對此:

template <typename T>
void read(T&);

兩者完全不同! 在前一種情況下,可能無法進行扣除。

我只是簡單地將聲明放在前面,其余的進行編譯。 我不是一名語言律師,但我相信gcc在這里是正確的。 對您的代碼進行一些小的修改,以使其在沒有警告的情況下進行編譯。

//Forward declarations needed:
void read(std::string& str);
template <typename T> void read(std::vector<T>& vec);

// mostly your original code
    template <typename T>
T read()
{
    T value;
    read(value); // gcc compile error here
    return value;
}

void read(std::string& )
{
}

    template <typename T>
void read(std::vector<T>& )
{
}

int main() {

    read<std::string>();
    read<std::vector<int>>();
    read<std::vector<float>>();

    return 0;
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM