The question is pretty clear. The following gives the reason why I think these expressions might yield undefined behavior. I would like to know whether my reasoning is right or wrong and why.
Short read:
(IEEE 754) double
is not Cpp17LessThanComparable since <
is not a strict weak ordering relation due to NaN
. Therefore, the Requires elements of std::min<double>
and std::max<double>
are violated.
Long read:
All references follow n4800. Specifications of std::min
and std::max
are given …
c++ floating-point undefined-behavior c++-standard-library language-lawyer
编译器(clang-5.0.0
,GCC-7.3
,ICC-18
和MSVC-19
)发散的成员的可访问性WRT A
下面.
class A {
template <class> static constexpr int f() { return 0; }
template <int> struct B {};
template <class T> using C = B<f<T>()>;
};
Run Code Online (Sandbox Code Playgroud)
确实,请考虑以下用法:
template <class T> using D = A::C<T>;
int main() {
// | clang | gcc | icc | msvc
(void) A::f<int>(); // 1: | f | f | f | f, (C)
(void) A::B<0>{}; // 2: | B | | B | …
Run Code Online (Sandbox Code Playgroud) 在我的一个研究项目中,我正在编写C ++代码。但是,生成的程序集是项目的关键点之一。C ++不提供对标志操作指令的直接访问,特别是对C ++,ADC
但只要编译器足够聪明地使用它,这就不成问题。考虑:
constexpr unsigned X = 0;
unsigned f1(unsigned a, unsigned b) {
b += a;
unsigned c = b < a;
return c + b + X;
}
Run Code Online (Sandbox Code Playgroud)
Variable c
是一种变通方法,可让我掌握进位标志并将其添加到b
和中X
。看起来我很幸运,(g++ -O3
,版本9.1)生成的代码是这样的:
f1(unsigned int, unsigned int):
add %edi,%esi
mov %esi,%eax
adc $0x0,%eax
retq
Run Code Online (Sandbox Code Playgroud)
对于X
我测试过的所有值,代码均与上面相同(当然,立即$0x0
更改的立即值除外)。但我发现了一个例外:何时X == -1
(或0xFFFFFFFFu
或~0u
,...的拼写方式并不重要)生成的代码为:
f1(unsigned int, unsigned int):
xor %eax,%eax
add %edi,%esi
setb %al
lea -0x1(%rsi,%rax,1),%eax
retq …
Run Code Online (Sandbox Code Playgroud) 我的一个朋友问我"如何使用CRTP替换多级继承中的多态".更准确地说,在这种情况下:
struct A {
void bar() {
// do something and then call foo (possibly) in the derived class:
foo();
}
// possibly non pure virtual
virtual void foo() const = 0;
}
struct B : A {
void foo() const override { /* do something */ }
}
struct C : B {
// possibly absent to not override B::foo().
void foo() const final { /* do something else */ }
}
Run Code Online (Sandbox Code Playgroud)
我和我的朋友都知道CRTP不是多态的替代品,但我们对可以使用这两种模式的情况感兴趣.(为了这个问题,我们对每种模式的利弊都不感兴趣.)
之前已经问过这个问题,但事实证明作者想要实现命名参数idiom …
假设我们实现了一个string
表示字符串的类.然后我们想要添加一个operator+
连接两个string
s的,并决定通过表达式模板实现它,以避免在执行时进行多次分配str1 + str2 + ... + strN
.
运算符将如下所示:
stringbuilder<string, string> operator+(const string &a, const string &b)
Run Code Online (Sandbox Code Playgroud)
stringbuilder
是一个模板类,它反过来重载operator+
并具有隐式string
转换运算符.几乎是标准的教科书练习:
template<class T, class U> class stringbuilder;
template<> class stringbuilder<string, string> {
stringbuilder(const string &a, const string &b) : a(a), b(b) {};
const string &a;
const string &b;
operator string() const;
// ...
}
// recursive case similar,
// building a stringbuilder<stringbuilder<...>, string>
Run Code Online (Sandbox Code Playgroud)
只要有人这样做,上述实现就完美无缺
string result = str1 …
Run Code Online (Sandbox Code Playgroud) 在一个相关的问题中,它说没有指向非成员const函数的指针.另外,C++ 11 8.3.5/6说
cv-qualifier-seq在函数声明符中的作用与在函数类型之上添加cv-qualification不同.在后一种情况下,忽略cv限定符.[注意:具有cv-qualifier-seq的函数类型不是cv限定类型; 没有cv限定的函数类型. - 尾注]
如果我理解正确,这意味着没有非成员const函数.(虽然这些函数不是常量,但不能按照3.10/6进行修改).特别是,指向const函数的指针是没有意义的.
但是,似乎有些编译器会在类型推导上下文中创建指向const函数的指针.例如,考虑代码:
#include <iostream>
void f() {}
template <typename T> void g( T*) { std::cout << "non const" << std::endl; }
template <typename T> void g(const T*) { std::cout << "const " << std::endl; }
int main() {
g(f);
}
Run Code Online (Sandbox Code Playgroud)
当使用GCC和Intel编译时,代码输出"非常量",正如我从上面的引用中所期望的那样.但是,使用Clang和Visual Studio编译时输出为"const".
我的解释是否正确?
更新:
在评论之后,我试图澄清我不是在讨论const成员函数.我对非成员函数感兴趣(但同样的参数也可能适用于非静态成员函数).我也改变了问题标题,使其更精确.
与g(f)
上面提到的解决方案一致,以下行对于GCC和英特尔而言是非法的,但对于Clang和Visual Studio则不行
const auto* ptr = &f;
Run Code Online (Sandbox Code Playgroud)
更新2:
我同意Andy Prowl的解释,并选择了他的答案.然而,在那之后,我意识到这个问题是CWG 公开问题.
考虑:
#include <type_traits>
template <typename T>
struct A {
~A() requires(std::is_void_v<T>);
//~A() requires(!std::is_void_v<T>) = default;
};
template struct A<int>;
Run Code Online (Sandbox Code Playgroud)
我认为一个类不可能没有析构函数,gcc 13.2 似乎同意我的观点,但 clang 17.0 和 msvc 19.38 不同意并抱怨:
[clang] error: no viable destructor found for class 'A<int>'
[msvc ] error C7653: 'A<int>': failed to select a destructor for the class
Run Code Online (Sandbox Code Playgroud)
(将注释行添加到代码中可以解决此问题并使所有编译器满意。)
哪些编译器是正确的?标准中的相关引用是什么?
众所周知,在某些条件下,编译器可能会忽略对复制构造函数的调用.但是,标准清楚地表明编译器只能自由地更改运行时行为(调用或不调用复制构造函数),但执行转换就像调用复制构造函数一样.特别是,编译器检查是否有一个有效的复制构造函数来调用.
我遇到了一个情况,可能会省略析构函数调用,但编译器在是否需要存在有效的析构函数方面存在差异.
这是一个完整的示例,说明了此问题可能如何发生以及编译器的行为方式有何不同.
template <typename T>
struct A {
~A() { (void) sizeof(T); }
};
struct B; // defined elsewhere.
struct C {
A<B> x, y;
~C(); // defined in a TU where B is complete.
};
int main() {
C c;
}
Run Code Online (Sandbox Code Playgroud)
编译时main()
编译生成C
的默认构造函数.此构造函数默认首先初始化x
然后再初始化y
.如果在y
施工期间抛出异常,则x
必须销毁.生成的代码如下所示:
new ((void*) &this->x) A<B>; // default initializes this->x.
try {
new ((void*) &this->y) A<B>; // default initializes this->y.
}
catch (...) {
(this->x).~A<B>(); // …
Run Code Online (Sandbox Code Playgroud) 我用GCC,Clang,ICC和VS测试了以下代码:
void f() {}
void g(void (&&)()) { }
int main() {
g(f);
}
Run Code Online (Sandbox Code Playgroud)
正如我们所看到的,g
采用右值引用但是f
是左值,并且通常,右值引用不能绑定到左值.这正是ICC抱怨的:
error: an rvalue reference cannot be bound to an lvalue
Run Code Online (Sandbox Code Playgroud)
VS也会出错,但出于另一个原因:
error C2664: 'void h(void (__cdecl &&)(void))' : cannot convert parameter 1 from 'void (__cdecl *)(void)' to 'void (__cdecl &&)(void)'
Run Code Online (Sandbox Code Playgroud)
这告诉我VS立即执行函数到指针的转换,而不是直接绑定引用f
.值得一提的是,如果我替换g(f)
为g(&f)
那么四个编译器会产生同样的错误.
最后,GCC和Clang接受了代码,我相信它们是正确的.我的推理基于8.5.3/5
对类型"cv1 T1"的引用由类型"cv2 T2"的表达式初始化为
- 如果参考是左值参考[...]
- 否则,[...] 参考应为右值参考.
- 如果初始化表达式是 [...] 函数左值 [...]
然后 …
我一直在努力防止用户使用没有智能指针的类.因此,强制它们使对象被智能指针分配和管理.为了得到这样的结果,我尝试了以下方法:
#include <memory>
class A
{
private :
~A {}
// To force use of A only with std::unique_ptr
friend std::default_delete<A>;
};
Run Code Online (Sandbox Code Playgroud)
如果您只希望您的类用户能够通过操作类的实例,那么这项工作非常好std::unique_ptr
.但它不起作用std::shared_ptr
.所以我想知道你是否有任何想法来获得这样的行为.我发现的唯一解决方案是执行以下操作(使用friend std::allocator_traits<A>;
效率不高):
#include <memory>
class A
{
private :
~A {}
// For std::shared_ptr use with g++
friend __gnu_cxx::new_allocator<A>;
};
Run Code Online (Sandbox Code Playgroud)
但这种解决方案不可移植.也许我做错了.