我的班级中有一个名为template<typename T> T Get(/*stuff*/);
的模板API函数。我的源文件为T
类型的某个列表实现了此函数。如果用户想要使用我尚未实现的类型,那么我希望结果是 compile 错误,而不是链接器错误。我还不太关心编译信息。这是我到目前为止所得到的:
MyClass.h
#pragma once
#define API_TYPE(X) \
template<> struct Implemented<X> : public API<X> {}
namespace MyClassAPI
{
template<typename T> struct API
{
static T Get(const T&);
};
template<typename T> struct Implemented {};
API_TYPE(bool);
}
class MyClass
{
template<typename T> friend struct MyClassAPI::API;
public:
template<typename T> T Get(const T& t) const
{
return MyClassAPI::Implemented<T>::Get(t);
}
};
MyClass.cpp
#include "MyClass.h"
namespace MyClassAPI
{
template<typename T> T API<T>::Get(const T& t) { return t; }
//template struct API<bool> //Why do I need this?
}
的main.cpp
#include "MyClass.h"
#include <iostream>
#include <cassert>
using namespace std;
// Main File
int main() {
MyClass c;
cout << "Getting true: " << c.Get(true) << endl;
return 0;
}
所以我的问题是关于MyClass.cpp中的一行。为什么我需要使用API<bool>
在源文件中复制template struct API<bool>;
显式声明?当Implemented<bool> : public API<bool>
继承它时,它不应该知道从头文件的声明中扩展模板函数定义吗?
另外,有没有办法在不声明我接受的类型列表两次的情况下执行此操作?
没有这行的错误:
g++ -Wfatal-errors -Werror -std=c++11 -g -O0 -Wall -c MyClass.cpp -o MyClass.o
g++ -Wfatal-errors -Werror -std=c++11 -g -O0 -Wall test.cpp MyClass.o -o test
/tmp/ccVxp4F3.o: In function `bool MyClass::Get<bool>(bool const&) const':
MyClass.h:25: undefined reference to `MyClassAPI::API<bool>::Get(bool const&)'
collect2: error: ld returned 1 exit status
make: *** [test] Error 1
答案 0 :(得分:2)
您的模板存在的问题是您在单独的翻译单元中定义了它的成员,因此它们对main.cpp
不可见,并且C ++不支持单独的模板翻译。< / p>
当您使用template struct API<bool>;
时,请求编译器为API<T>
显式实例化 T = bool
。但是,在这样做时,您还应该让其他翻译单元知道实例化发生在其他地方,方法是在头文件中使用模板声明使用类似的指令:
extern template struct API<bool>;
否则,将Get
的定义移到头文件中或在头文件中包含.cpp文件(不推荐),并依赖编译器为您实例化模板成员。
关于限制可行模板参数列表,我建议一种基于模板元编程的方法
首先,我们定义一些工具来检查类型是否属于类型列表,让我们称之为is_in<DesiredType, ViableTypes...>
。它将接受要查找的类型T
作为模板参数,并接受用于搜索的类型InTypes...
的列表,并通过编译时可用的静态布尔成员提供结果。
它在列表上实现为一个简单的递归算法,在DesiredType
之前检查列表的每个元素,直到找到DesiredType
或满足列表的结尾:
#include <type_traits>
template<typename...> struct is_in;
template<typename T, typename InType, typename... InTypes> struct is_in<T, InType, InTypes...> {
static constexpr bool value = std::is_same_t<T, InType> ||
is_in<T, InTypes...>::value;
}
template<typename T> struct is_in<T> {
static constexpr bool value = false;
}
现在,拥有is_in
,我们可以使用static_assert
并明确指定模板的可行类型:
template<typename T> struct API
{
// Only allow API<bool> and API<int>
static_assert(is_in<T, bool, int>::value, "invalid template type for API<T>");
static T Get(const T&);
};
答案 1 :(得分:1)
你差不多了。
您需要更新MyClass.h
以提供几个函数的显式实例化,并在MyClass.cpp
中实现它们。
在.h文件中,添加:
// Explicit instantiations
namespace MyClassAPI
{
template<> int API<int>::Get(const int&);
template<> double API<double>::Get(const double&);
}
在.cpp文件中,添加:
// Implement the explicit instantiations
namespace MyClassAPI
{
template<> int API<int>::Get(const int& in)
{
// Add whatever logic that makes sense for this type.
return 2*in;
}
template<> double API<double>::Get(const double& in)
{
// Add whatever logic that makes sense for this type.
return 10*in;
}
}
这是工作代码的单个文件版本:
#define API_TYPE(X) \
template<> struct Implemented<X> : public API<X> {}
namespace MyClassAPI
{
template<typename T> struct Implemented;
template<typename T> struct API
{
static T Get(T const&);
};
API_TYPE(int);
API_TYPE(double);
}
class MyClass
{
template<typename T> friend struct MyClassAPI::API;
public:
template<typename T> T Get(const T& t) const
{
return MyClassAPI::Implemented<T>::Get(t);
}
};
// Explicit instantiations
namespace MyClassAPI
{
template<> int API<int>::Get(const int&);
template<> double API<double>::Get(const double&);
}
#include <iostream>
int main()
{
MyClass a;
std::cout << a.Get<int>(10) << std::endl;
std::cout << a.Get<double>(10) << std::endl;
// Does not work. Produces compiler error.
// std::cout << a.Get<float>(10) << std::endl;
}
// Implement the explicit instantiations
namespace MyClassAPI
{
template<> int API<int>::Get(const int& in)
{
return 2*in;
}
template<> double API<double>::Get(const double& in)
{
return 10*in;
}
}
输出:
20
100
<强>更新强>
这是一个多文件版本:
MyClass.h:
#pragma once
#define API_TYPE(X) \
template<> struct Implemented<X> : public API<X> {}
namespace MyClassAPI
{
template<typename T> struct Implemented;
template<typename T> struct API
{
static T Get(T const&);
};
API_TYPE(int);
API_TYPE(double);
}
class MyClass
{
template<typename T> friend struct MyClassAPI::API;
public:
template<typename T> T Get(const T& t) const
{
return MyClassAPI::Implemented<T>::Get(t);
}
};
// Explicit instantiations
namespace MyClassAPI
{
template<> int API<int>::Get(const int&);
template<> double API<double>::Get(const double&);
}
MyClass.cc:
#include "MyClass.h"
// Implement the explicit instantiations
namespace MyClassAPI
{
template<> int API<int>::Get(const int& in)
{
return 2*in;
}
template<> double API<double>::Get(const double& in)
{
return 10*in;
}
}
main.cc:
#include <iostream>
#include "MyClass.h"
int main()
{
MyClass a;
std::cout << a.Get<int>(10) << std::endl;
std::cout << a.Get<double>(10) << std::endl;
// Does not work.
// std::cout << a.Get<float>(10) << std::endl;
}
它也可以成功构建并产生相同的结果。