跨文件共享的C++模板特化定义

4
我有一个模板类"Extra",在"extra.h"中定义了一个函数"doSomething",并且我定义了两个"doSomething"的特化版本。两个不同的函数创建了类型为"Extra"的对象,每个对象都具有不同的类型参数,并分别调用其中一个特化版本。两个客户端函数"client1"和"client2"分别在两个文件"client1.cpp"和"client2.cpp"中定义。在第三个文件中,"main"先调用"client1",再调用"client2"。现在,"client1.cpp"和"client2.cpp"都#include "extra.h"。我得到一个链接错误,指出"doSomething"有(2)个多重定义。当然,如果我将"client1"和"client2"的定义放入单个源文件中,则不会有此问题。有没有办法保留我的不同文件安排的方式来处理"client1"和"client2"?以下是我的代码。谢谢!
// extra.h
#ifndef EXTRA_H
#define EXTRA_H

template <typename T>
class Extra
{
    public:
        Extra(T);
        ~Extra();
        T doSomething(T);
    private:
        Extra() {}
        T m_value;
};

template <typename T> Extra<T>::Extra(T input) : m_value{input} {}

template <typename T> Extra<T>::~Extra() {}

template <> int Extra<int>::doSomething(int input)
{
    return input * m_value;
}

template <> double Extra<double>::doSomething(double input)
{
    return input + m_value;
}

template <typename T> T Extra<T>::doSomething(T input)
{
    return input;
}
#endif

// client1.cpp
#include "extra.h"
#include <iostream>

void client1()
{
    std::cout << "In client1." << std::endl;
    Extra<int> extra(2);
    int res = extra.doSomething(3);
    std::cout << "Value: " << res << std::endl;
}

// client2.cpp
#include "extra.h"
#include <iostream>

void client2()
{
    std::cout << "In client2." << std::endl;
    Extra<double> extra(2.0);
    double res = extra.doSomething(2.0);
    std::cout << "Value: " << res << std::endl;
}

// main.cpp
#include <iostream>

void client1();
void client2();

int main()
{
    std::cout << "In main." << std::endl;
    client1();
    client2();
    return 0;
}

为了完整起见,这里是我的链接器错误:

$ g++ -std=c++11 -o client client1.cpp client2.cpp main.cpp 
duplicate symbol __ZN5ExtraIiE11doSomethingEi in:
    /var/folders/mf/jdvfkpms609206zpz8x5237r0000gn/T/client1-6fa7ed.o
    /var/folders/mf/jdvfkpms609206zpz8x5237r0000gn/T/client2-387444.o
duplicate symbol __ZN5ExtraIdE11doSomethingEd in:
    /var/folders/mf/jdvfkpms609206zpz8x5237r0000gn/T/client1-6fa7ed.o
    /var/folders/mf/jdvfkpms609206zpz8x5237r0000gn/T/client2-387444.o
ld: 2 duplicate symbols for architecture x86_64
1个回答

3

你需要将doSomething标记为内联函数:

template <> inline int Extra<int>::doSomething(int input)
{
    return input * m_value;
}

template <> inline double Extra<double>::doSomething(double input)
{
    return input + m_value;
}

非常感谢!我之前已经尝试过了,但是我忘记了你必须将定义标记为“内联”,而不仅仅是声明。 - Amittai Aviram
@AmittaiAviram,使用这个提议的解决方案问题已经解决了,对吗? - Pavel P
是的!现在,这只是一个玩具示例,当然,我必须尝试将其应用于更加复杂的实际情况。我想一般原则是您必须内联每个函数专业化,以便编译器将代码移动到调用者中,并且它将不再出现为多个翻译单元中的不同函数定义。再次感谢! - Amittai Aviram
@AmittaiAviram 一般来说,inline 实际上是一个提示链接器的暗示,告诉它同一个函数可能有多个副本。当你直接在类内定义一个函数时,它会被隐式地视为内联函数。当你在头文件中定义函数并且不属于某个类(就像你所做的那样),你几乎总是需要显式地标记它们为内联函数,以避免链接错误。编译器是否将其内联是另一个问题。 - Pavel P

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接