2017-05-19 89 views
0

我想使用static_assert强制失败。如果您尝试以特定方式实例化特定的模板化函数,我想生成编译器错误。我可以让它工作,但它真的很难看。有没有更简单的方法来做到这一点?什么是调用static_assert(false)的正确方法?

这是我第一次尝试。这根本不起作用。即使没有人试图使用这个函数,它总是会产生一个错误。

template< class T > 
void marshal(std::string name, T *value) 
{ 
    static_assert(false, "You cannot marshal a pointer."); 
} 

这是我的第二次尝试。它实际上工作。如果你不打电话给你,你不会有错误。如果你确实调用了这个,你会得到一条非常可读的错误消息,指向这条线并指向试图实例化它的代码。

template< class T > 
void marshal(std::string name, T *value) 
{ 
    static_assert(std::is_pod<T>::value && !std::is_pod<T>::value, "You cannot marshal a pointer."); 
} 

问题是这个代码最好是丑陋的。它看起来像一个黑客。恐怕下一次我改变优化级别,升级编译器,打喷嚏等时,编译器会意识到第二种情况与第一种情况是一样的,他们都会停止工作。

有没有更好的方法来做我想做的事情?

这是一些上下文。我想有几个不同版本的marshal(),它们适用于不同的输入类型。我想要一个使用模板作为默认情况的版本。我想另一个明确禁止除char *之外的指针。

void marshal(std::string name, std::string) 
{ 
    std::cout<<name<<" is a std::string type."<<std::endl; 
} 

void marshal(std::string name, char *string) 
{ 
    marshal(name, std::string(string)); 
} 

void marshal(std::string name, char const *string) 
{ 
    marshal(name, std::string(string)); 
} 

template< class T > 
void marshal(std::string name, T value) 
{ 
    typedef typename std::enable_if<std::is_pod<T>::value>::type OnlyAllowPOD; 
    std::cout<<name<<" is a POD type."<<std::endl; 
} 

template< class T > 
void marshal(std::string name, T *value) 
{ 
    static_assert(false, "You cannot marshal a pointer."); 
} 

int main (int argc, char **argv) 
{ 
    marshal(“should be pod”, argc); 
    marshal(“should fail to compile”, argv); 
    marshal(“should fail to compile”, &argc); 
    marshal(“should be std::string”, argv[0]); 
} 
+0

为什么'typedef的类型名称的std :: enable_if'? – cpplearner

+0

@cpplearner有些人在模板的可选参数中隐藏了enable_if。这似乎更好,因为来电者不能意外地填写这个(假的)参数。 –

回答

4

有没有办法做到这一点。你可能可以使它在你的编译器上工作,但是生成的程序不正常,不需要诊断。使用=delete

template< class T > 
void marshal(std::string name, T *value) = delete; 
+0

有趣。我只看到=删除用于删除默认方法,如复制构造函数。我甚至不知道你可以这样做。我必须试一试。 –

2

你所要做的,是注定要形成不良的(即使你的解决方法可能会失败)根据[temp.res]/8(重点煤矿):

知道哪个名称是类型名称允许的语法要检查的每个模板 。 该方案是形成不良的,没有诊断需要,如果
- 没有有效的专业化可以为模板或constexpr的子语句生成,如果一个模板和 模板内声明不会被实例化,或(...)

+0

我昨晚读了10次。今天早上再次。我仍然不明白它的意思。 :( –

+1

不幸的是,标准是使用非常精确但有时难以理解的措辞编写的......基本上它说你不能创建一个无法实例化的模板(无论它是结构模板还是函数模板都无所谓)通过任何参数......换句话说,必须至少有一个参数集可以被实例化为模板 –

2

依托的矛盾是不是最好确实,但有一个简单的方法:

template <class...> 
struct False : std::bool_constant<false> { }; 

template <class T> 
void bang() { 
    static_assert(False<T>{}, "bang!"); 
} 

为什么这不是在下降“无效专业化”案例?
嗯,因为你可以真正发挥有效的专业化,用代码是下半年:

template <> 
struct False<int> : std::bool_constant<true> { }; 

int main() { 
    bang<int>(); // No "bang"! 
} 

当然,没有人真正去专门False打破真正的代码的断言,但这是可能的:)

+0

如果编译器知道没有有效的特化(即,“False”从不专门化,因为它有源代码代码),那么它不会证明你的程序是不健康的,不需要诊断吗?也就是说,你确定这个规则避免了这个规则,还是只是让它更难注意到? – Yakk

+0

@Yakk我认为这正在扩展它。 * *可以是专用的,它不在当前程序中,不在当前程序中AFAICT的范围之内。如果我动态加载另一个具有专门的'False'的二进制文件,那会怎样?我的计划再次形成了? – Quentin

+0

所以,你不确定?我得到了希望能够静态声明错误的合理性,但是标准被写为说“不”。当你靠近边缘时,标准的确切用词很重要。它指出没有任何论据会使专业化实例化;直到'False :std :: bool_constant {}'实际上对某些''存在?'它确实没有参数会使特化实例化。我可能是错的;我经常是。但我会对“形成病态,不需要诊断”有充分的警惕。 – Yakk

1

我不明白你为什么要把template< class T > void marshal(std::string name, T *value)放在第一位。这应该只是主模板中的一个static_assert。

也就是说,你应该主模板的定义修改为

template< class T > 
void marshal(std::string name, T value) 
{ 
    static_assert(std::is_pod<T>::value); 
    static_assert(!std::is_pointer<T>::value); 
    std::cout<<name<<" is a POD type."<<std::endl; 
} 
+0

谢谢。我只是在寻找类似的解决方案。有很多选择,很难选择正确的选项。 –

+0

我选择了一个不同的答案,但是你的答案接近第二。 =删除选项似乎对读者来说更加明显。但是,您的答案似乎更加灵活,未来我可能需要这个技巧。谢谢。 –

相关问题