P0292R1 constexpr if已经included,正在进行C ++ 17。它似乎很有用(并且可以取代SFINAE的使用),但关于static_assert
形成错误,在虚假分支中无需诊断的评论让我害怕:
Disarming static_assert declarations in the non-taken branch of a
constexpr if is not proposed.
void f() {
if constexpr (false)
static_assert(false); // ill-formed
}
template<class T>
void g() {
if constexpr (false)
static_assert(false); // ill-formed; no
// diagnostic required for template definition
}
我认为完全禁止在constexpr中使用static_assert
if(至少是假/非分支,但实际上这意味着它不安全或有用事情要做。)
这是如何从标准文本中产生的?我发现提案措辞中没有提及static_assert
,而C ++ 14 constexpr函数允许static_assert
(详见cppreference:constexpr)。
它是否隐藏在这个新句子中(6.4.1之后)? :
当constexpr if语句出现在模板化实体中时, 在封闭模板或通用lambda的实例化期间, 丢弃的语句没有实例化。
从那时起,我假设它也被禁止,不需要诊断,调用其他constexpr(模板)函数,其中某处向下调用图可能调用static_assert
。
底线:
如果我的理解是正确的,那么对constexpr if
的安全性和有用性施加相当严格的限制,因为我们必须知道(来自文档或代码检查)static_assert
的使用template< typename T>
constexpr void other_library_foo(){
static_assert(std::is_same<T,int>::value);
}
template<class T>
void g() {
if constexpr (false)
other_library_foo<T>();
}
int main(){
g<float>();
g<int>();
}
1}?我的担忧是否错位?
更新
此代码在没有警告的情况下编译(clang head 3.9.0)但是我理解格式错误,无需诊断。是否有效?
if (Modernizr.geolocation) {
// device supports geolocation
} else {
// device doesn't support geolocation
}
答案 0 :(得分:23)
这是关于模板的完善规则 - 允许编译器诊断template<class> void f() { return 1; }
的规则。 [temp.res]/8新更改加粗:
该程序格式错误,无需诊断,如果:
- 无法为模板或子语句生成有效的专业化 一个
constexpr if
语句([stmt.if]) 模板,模板未实例化,或- [...]
对于包含static_assert
且条件不相关且评估为false
的模板,无法生成有效的专业化,因此该程序是格式错误的NDR。
static_assert
具有可以评估至少一种类型的true
的从属条件不受影响。
答案 1 :(得分:7)
Edit: I'm keeping this self-answer with examples and more detailed explanations of the misunderstandings that lead to this questions. The short answer by T.C. is strictly enough.
After rereading the proposal and on static_assert
in the current draft, and I conclude that my worries were misguided. First of all, the emphasis here should be on template definition.
ill-formed; no diagnostic required for template definition
If a template is instantiated, any static_assert
fire as expected. This presumably plays well with the statement I quoted:
... a discarded statement is not instantiated.
This is a bit vague to me, but I conclude that it means that templates occurring in the discarded statement will not be instantiated. Other code
however must be syntactically valid. A static_assert(F)
, [where F is false, either literally or a constexpr value] inside a discarded if constexpr
clause will thus still 'bite' when the template containing the static_assert
is instantiated. Or (not required, at the mercy of the compiler) already at declaration if it's known to always be false.
Examples: (live demo)
#include <type_traits>
template< typename T>
constexpr void some_library_foo(){
static_assert(std::is_same<T,int>::value);
}
template< typename T>
constexpr void other_library_bar(){
static_assert(std::is_same<T,float>::value);
}
template< typename T>
constexpr void buzz(){
// This template is ill-formated, (invalid) no diagnostic required,
// since there are no T which could make it valid. (As also mentioned
// in the answer by T.C.).
// That also means that neither of these are required to fire, but
// clang does (and very likely all compilers for similar cases), at
// least when buzz is instantiated.
static_assert(! std::is_same<T,T>::value);
static_assert(false); // does fire already at declaration
// with latest version of clang
}
template<class T, bool IntCase>
void g() {
if constexpr (IntCase){
some_library_foo<T>();
// Both two static asserts will fire even though within if constexpr:
static_assert(!IntCase) ; // ill-formated diagnostic required if
// IntCase is true
static_assert(IntCase) ; // ill-formated diagnostic required if
// IntCase is false
// However, don't do this:
static_assert(false) ; // ill-formated, no diagnostic required,
// for the same reasons as with buzz().
} else {
other_library_bar<T>();
}
}
int main(){
g<int,true>();
g<float,false>();
//g<int,false>(); // ill-formated, diagnostic required
//g<float,true>(); // ill-formated, diagnostic required
}
The standard text on static_assert
is remarkably short. In standardese, it's a way to make the program ill-formed with diagnostic (as @immibis also pointed out):
7.6 ... If the value of the expression when so converted is true, the declaration has no effect. Otherwise, the program is ill-formed, and the resulting diagnostic message (1.4) shall include the text of the string-literal, if one is supplied ...
答案 2 :(得分:6)
C ++ 20现在使class ViewController: BaseViewController {
override func viewDidLoad() {
super.viewDidLoad()
backButton.addTarget(self, action: #selector(back), for: .touchUpInside)
navigationItem.leftBarButtonItem = UIBarButtonItem(customView: backButton)
}
@objc func back(index : Int) {
self.navigationController?.popViewController(animated: true)
}
}
的{{1}}分支中的static_assert
更短,因为它允许模板lambda参数。因此,为了避免出现格式错误的情况,我们现在可以定义一个带有else
模板非类型参数的lambda,以用于触发if constexpr
。我们立即使用bool
调用lambda,但由于如果未采用其static_assert
分支将不会实例化lambda,因此除非实际采用()
,否则断言不会触发:< / p>
else
答案 3 :(得分:1)
您的自我解答,也可能是T.C.不太正确。
首先,“即使在if constexpr
中,两个静态断言也会触发”的句子是不正确的。它们不会是因为if constexpr
条件取决于模板参数。
您可以看到,如果您在示例代码中注释掉static_assert(false)
语句和buzz()
的定义,则static_assert(!IntCase)
不会触发,并且会编译。
此外,在丢弃的AlwaysFalse<T>::value
中,! std::is_same_v<T, T>
或constexpr if
are allowed之类的东西(并且没有任何作用),即使没有T
他们认为是正确的。
我认为“无法生成有效的专业化”在标准中用词不好(除非cppreference是错误的;否则T.C.是正确的)。应该说“可以生成”,并进一步阐明“可以”的含义。
这与以下问题有关:AlwaysFalse<T>::value
和! std::is_same_v<T, T>
are equivalent在这种情况下(这是对此答案的评论)。
我会说它们是正确的,因为它是“可以”而不是“可以”,并且在实例化时对于所有类型都是错误的。
std::is_same
与非标准包装器之间的关键区别在于,非标准包装器在理论上可以是专用的(感谢cigien,指出这一点并提供链接)。
是否形成格式错误的NDR的问题也关键取决于模板是否被实例化,只是为了使其完全清楚。