我们有复杂的模板类,其中包含一些不适用于某些策略或类型的方法。因此,当我们检测到这些类型时(在编译时,使用类型特征),我们使用一条好消息来激活一个静态断言。
现在我们也做了很多手动模板实例化。部分原因是这些方法被迫编译器对语法进行语法检查。它还减少了库用户的编译时间。问题是静态断言总是被触发,因此我们无法手动实例化有问题的模板类。
有解决方法吗?
编辑:为了更清楚,这里有一个例子(在这种情况下显式实例化将在someFunc1()上失败:
// header
template <typename T>
class someClass
{
void someFunc() {}
void someFunc1() { static_assert(false, assertion_failed); }
};
// source
template someClass<int>; // Explicit instantiation
EDIT2:这是另一个例子。这次你可以编译它看看我的意思。首先立即编译。代码应该编译。然后取消注释[2] ,并且应该触发静态断言。现在评论[2] 和取消注释[1] 。静态断言将触发,因为您明确地实例化模板。我想避免删除显式实例化,因为它带来的好处(参见上面的好处)。
namespace Loki
{
template<int> struct CompileTimeError;
template<> struct CompileTimeError<true> {};
}
#define LOKI_STATIC_CHECK(expr, msg) \
{ Loki::CompileTimeError<((expr) != 0)> ERROR_##msg; (void)ERROR_##msg; }
template <typename T>
class foo
{
public:
void func() {}
void func1() { LOKI_STATIC_CHECK(sizeof(T) == 4, Assertion_error); }
};
template foo<int>;
//template foo<double>; // [1]
int main()
{
foo<int> a;
a.func1();
foo<double> b;
//b.func1(); //[2]
return 0;
}
答案 0 :(得分:3)
你不能同时拥有:你不能有静态断言来阻止实例化和显式实例化类型!这是一个明显的矛盾。然而,你可以拥有的是条件包含的功能,即使它在某种程度上是一种痛苦:如果某些类型不支持某个成员函数,你可以将这个函数移动到有条件地拥有它的基类中。这样您就不会使用静态断言,只需删除成员函数即可。我意识到这引入了有趣的其他问题,例如关于成员变量的位置,但我认为在你描述的这种情况下,这是你能得到的最好的。
以下是一个简单示例:
template <typename T, bool = std::numeric_limits<T>::is_integer> struct foo_base;
template <typename T> struct foo_base<T, false> { /* intentionally left blank */ };
template <typename T> struct foo_base<T, true> { void foo() { /*...*/ } };
template <typename T>
struct Foo: foo_base<T> { /* .... */ };
template struct Foo<int>; // will have foo()
template struct Foo<double>; // will not have foo()
答案 1 :(得分:2)
好吧,所以如果你使用显式实例化强制所有方法的实例化,你就无法逃脱任何编译时间技巧来阻止实例化有问题的方法,例如enable_if
。将错误移动到运行时很容易,但这是不可取的。
我认为您可以做的最好的事情就是将错误移动到链接时间,这将静态地确保程序不包含可能会调用禁止函数的代码路径,但错误消息对于任何不了解你所施加的限制的人。无论如何,解决方案是声明禁止成员函数的特化,但不定义它们:
template<typename T>
struct Foo {
void bar() {
std::cout << "bar\n";
}
void baz() {
std:: cout << "baz\n";
}
};
template<> void Foo<int>::baz(); // use of Foo<int>::baz() will resolve to this specialization, and linking will fail
template struct Foo<int>;
template struct Foo<char>;
int main() {
Foo<int> f;
f.bar();
// f.baz(); // uncommenting this line results in an ugly link time error
Foo<char> b;
b.bar();
b.baz(); // works with Foo<char>
}
当客户端代码中出现错误时,静态断言不再有助于提供好的错误消息,但您可能希望将它们保留,因为如果您忘记提供专业化,它们将会触发。
答案 2 :(得分:1)
enable_if是一种灵活的机制,可用于精确模板方法定位,可能就是您所追求的。例如:
#include <string>
#include <iostream>
#include <boost/utility.hpp>
#include <boost/type_traits.hpp>
#include <boost/static_assert.hpp>
template <class T> class mywrapper
{
T _value;
template <class V>
typename boost::enable_if<boost::is_scalar<V>, void>::type printval_(V const& value)
{
BOOST_STATIC_ASSERT(boost::is_scalar<V>::value);
std::cout << "scalar: " << value << std::endl;
}
template <class V>
typename boost::enable_if<boost::is_compound<V>, void>::type printval_(V const& value)
{
BOOST_STATIC_ASSERT(boost::is_compound<V>::value);
std::cout << "compound: " << value << std::endl;
}
public:
mywrapper(T const& value):_value(value) { }
void printval() { printval_(_value); }
};
template class mywrapper<int>;
template class mywrapper<std::string>;
int main()
{
mywrapper<int> ival(333);
mywrapper<std::string> sval("test");
ival.printval();
sval.printval();
return 0;
}
答案 3 :(得分:0)
我没有机会按照bobah的建议测试enable_if
,但我确实提出了一个不需要提升的解决方案,并且在很大程度上满足了我原来的要求(我说 good 而不是完整,最后会解释)
解决方案是在代码上放置一个虚拟模板,如果在某些选定的类型下编译将会失败,并且在其他类型下很好。所以:
struct dummyStruct {};
#define DUMMY_TEMP typename dummy
#define DUMMY_PARAM dummyStruct
namespace Loki
{
template<int> struct CompileTimeError;
template<> struct CompileTimeError<true> {};
}
#define LOKI_STATIC_CHECK(expr, msg) \
{ Loki::CompileTimeError<((expr) != 0)> ERROR_##msg; (void)ERROR_##msg; }
template <typename T>
class foo
{
public:
void func() {}
template <typename T_Dummy>
void func1() { LOKI_STATIC_CHECK(sizeof(T) == 4, Assertion_error); }
};
template foo<int>;
template foo<double>; // [1]
int main()
{
foo<int> a;
a.func1<DUMMY_PARAM>();
foo<double> b;
//b.func1<DUMMY_PARAM>(); //[2] - this is a static error
return 0;
}
在我的所有模板代码中,这些类型的函数(即具有静态断言的函数或者在某些类型上工作,并且可能通过使用类型特征在其他类型上失败[在这种情况下,可以为不同的函数选择几个不同的函数]类型])是从客户端隐藏的。所以在我的实现中,添加额外的dummy parameter
是一个妥协。
作为奖励,它让我知道此功能仅供某些类型使用。此外,我最初的显式实例化问题是通过这种简单的技术解决的。