7

我们有复杂的模板类,其中一些方法不适用于某些策略或类型。因此,当我们检测到这些类型时(在编译时,使用 type-traits),我们会触发一个带有不错消息的静态断言。

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

有解决方法吗?

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

4 回答 4

3

好的,因此,如果您使用显式实例化来强制实例化所有方法,那么您将无法使用任何编译时技巧来阻止违规方法的实例化,例如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>
}

当客户端代码出现错误时,静态断言不再有助于提供漂亮的错误消息,但您可能希望保留它们,因为如果您忘记提供专业化,它们会触发。

于 2012-01-21T22:21:04.187 回答
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()
于 2012-01-21T23:51:19.000 回答
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;
}
于 2012-01-21T23:31:45.767 回答
0

我没有enable_if按照 bobah 的建议进行测试,但我确实提出了一个不需要提升的解决方案,并且在很大程度上满足了我的原始要求(我说而不是完整,将在最后解释)

解决方案是在代码上放置一个虚拟模板,如果在某些选定的类型下编译会失败,而在其他类型下则很好。所以:

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是一个不错的折衷方案。

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

于 2012-04-09T22:53:12.950 回答