我可以从手动模板实例化中排除一些方法吗?

时间:2012-01-21 21:59:09

标签: c++ templates static-assert

我们有复杂的模板类,其中包含一些不适用于某些策略或类型的方法。因此,当我们检测到这些类型时(在编译时,使用类型特征),我们使用一条好消息来激活一个静态断言。

现在我们也做了很多手动模板实例化。部分原因是这些方法被迫编译器对语法进行语法检查。它还减少了库用户的编译时间。问题是静态断言总是被触发,因此我们无法手动实例化有问题的模板类。

有解决方法吗?

编辑:为了更清楚,这里有一个例子(在这种情况下显式实例化将在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;
}

4 个答案:

答案 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是一个妥协。

作为奖励,它让我知道此功能仅供某些类型使用。此外,我最初的显式实例化问题是通过这种简单的技术解决的。

相关问题