如果你看一下get辅助函数std::tuple,你会注意到以下的重载:
template< std::size_t I, class... Types >
constexpr std::tuple_element_t<I, tuple<Types...> >&&
get( tuple<Types...>&& t );
Run Code Online (Sandbox Code Playgroud)
换句话说,当输入元组本身是右值引用时,它返回一个右值引用.为什么不按值返回,调用move函数体?我的论点如下:get的返回将被绑定到引用,或者绑定到一个值(它可能被绑定到我想的任何东西,但这不应该是一个常见的用例).如果它与一个值绑定,那么移动构造无论如何都会发生.所以你不会因为价值回归而失去一切.如果绑定到引用,则返回右值引用实际上可能不安全.举个例子:
struct Hello {
Hello() {
std::cerr << "Constructed at : " << this << std::endl;
}
~Hello() {
std::cerr << "Destructed at : " << this << std::endl;
}
double m_double;
};
struct foo {
Hello m_hello;
Hello && get() && { return std::move(m_hello); }
};
int main() {
const Hello & x = foo().get();
std::cerr << x.m_double; …Run Code Online (Sandbox Code Playgroud) 我最近在多种情况下遇到过这个问题,其中表达的一些观点让我感到惊讶。这是第一个简单的例子:
void f(std::vector<double> x) {};
Run Code Online (Sandbox Code Playgroud)
问题是:记录或描述f提供不抛出保证是否可以接受?同样,我怀疑由于异常不是从 f 的主体生成的,因此使用noexcept在技术上是合理的。但应该标记它吗noexcept?例如,一个优化版本以set某种方式发现添加模板化比较器不得抛出的要求很有用。它在编译时使用静态断言检测到这一点并导致错误。然而,有人可以为按值获取的向量编写一个比较器,并将其标记为 noexcept,并将其与此版本的set. 如果这导致了不良行为,那么这是容器作者的错吗?或者将比较器标记为 no except 的人?
再举一个涉及另一种异常保证类型的示例,请考虑:
void g(std::vector<double> x, std::unique_ptr<int> y);
Run Code Online (Sandbox Code Playgroud)
该功能能否提供强有力的保证?
std::vector<double> p{1.0, 2.0};
auto q = std::make_unique<int>(0);
bool func_successful = true;
try {
g(p, std::move(q));
}
catch (...) {
func_successful = false;
}
if (!func_successful)
assert(q);
Run Code Online (Sandbox Code Playgroud)
我认为,如果断言可能失败,那么 g 不会提供强有力的保证,因为q在调用 g 之前立即不为空(记住std::move实际上并没有移动任何东西)。它可能会失败:参数评估的顺序未指定,因此可能首先构造 y 清空 q,然后构造 x 并抛出。即使从技术上讲它不是发生在函数体中,而是发生在调用函数的行为中,该函数仍然对此负责吗?
编辑:我要提到 Herb Sutter 在这里讨论了这个问题:https://youtu.be/xnqTKD8uD64 ?t=1h11m56s 。他说,将这样的函数标记为 noexcept 是“有问题的”;我希望得到更详细的答复。
我最近在这里问了一个问题(用SFINAE检测实例方法constexpr),我试图在编译时进行一些constexpr检测.最后,我发现可以利用noexcept这个来做:任何常量表达式也是如此noexcept.所以我把以下机器放在一起:
template <class T>
constexpr int maybe_noexcept(T && t) { return 0; }
...
constexpr bool b = noexcept(maybe_noexcept(int{}));
Run Code Online (Sandbox Code Playgroud)
这是正常的,b正如您所期望的那样,因为零初始化int是一个常量表达式.它应该正确地产生零(如果我改为int其他适当的类型).
接下来,我想检查某些东西是否可constexpr移动构造.所以我这样做了:
constexpr bool b = noexcept(maybe_noexcept(int(int{})));
Run Code Online (Sandbox Code Playgroud)
而且,这适用于int或用户定义的类型.但是,这会检查该类型是否包含constexpr默认构造函数和constexpr移动构造函数.所以,为了解决这个问题,我尝试改为declval:
constexpr bool b = noexcept(maybe_noexcept(int(declval<int>())));
Run Code Online (Sandbox Code Playgroud)
这导致在bgcc 5.3.0 中为false(不能使用任何一个clang,因为clang不能正确地生成常量表达式noexcept).没问题,我说,一定是因为declval(有趣的是)没有标记constexpr.所以我写了自己的天真版本:
template <class T>
constexpr T&& constexpr_declval() noexcept;
Run Code Online (Sandbox Code Playgroud)
是的,与标准库的工作方式相比,这是天真的,因为它会阻塞虚空和可能的其他东西,但它现在很好.所以我再试一次:
constexpr bool b = noexcept(maybe_noexcept(int(constexpr_declval<int>())));
Run Code Online (Sandbox Code Playgroud)
这仍然不起作用,b总是假的.为什么这不被视为常数表达式?这是一个编译器错误,还是我不了解基本原理constexpr?似乎在constexpr和未评估的上下文之间存在一些奇怪的交互.
在python中,如果直接将可变类型作为默认参数,则会出现一个众所周知的边缘情况:
def foo(x=[]): return x
y = foo()
y.append(1)
print foo()
Run Code Online (Sandbox Code Playgroud)
通常的解决方法是将参数默认为None然后将其设置在正文中.然而,有3种不同的方法可以做到这一点,其中2种基本相同,但第三种是完全不同的.
def foo(x=None):
if x is None:
x = []
return x
Run Code Online (Sandbox Code Playgroud)
这就是我经常看到的.
def foo(x=None):
x = [] if x is None else x
return x
Run Code Online (Sandbox Code Playgroud)
语义相同.一条线更短,但有些人抱怨python的三元组是不自然的,因为它不是以条件开始并建议避免它.
def foo(x=None):
x = x or []
Run Code Online (Sandbox Code Playgroud)
这是最短的.我今天才了解到这种疯狂.我知道lisp所以这对我来说可能不像一些python程序员那么令人惊讶,但我从没想过这会在python中运行.这种行为是不同的; 如果你传递的东西不是None但是评估为false(比如False),它就不会覆盖默认值.如果默认值不评估false,则无法使用它,因此如果您有非空列表或dict默认值,则无法使用它.但是,根据我的经验,空列表/摘要是99%的感兴趣的案例.
关于哪个是最pythonic的想法?我意识到这里有一个观点要素,但我希望有人可以给出一个很好的例子或推理,以确定什么是最惯用的.与大多数社区相比,python倾向于强烈鼓励人们以某种方式做事,所以我希望这个问题及其答案即使不完全是黑白也会有用.
看起来这个代码不正确,因为我得到编译器错误.我试图理解为什么:
template <class ... Ts>
struct type_list{};
template <class ... Ts, class T_, class ... Ts_>
auto foo(type_list<Ts...>, Ts&&..., T_&&t, Ts_&&...) {
return sizeof(T_);
}
int main() {
std::cerr << foo(type_list<int>{}, 5, 5.0, 3);
return 0;
}
Run Code Online (Sandbox Code Playgroud)
clang产生以下错误:
example.cpp:16:16: error: no matching function for call to 'foo'
std::cerr << foo(type_list<int>{}, 5, 5.0, 3);
^~~
example.cpp:11:6: note: candidate template ignored: deduced conflicting types for parameter 'Ts'
(<int> vs. <>)
auto foo(type_list<Ts...>, Ts&&..., T_&&t, Ts_&&...) {
Run Code Online (Sandbox Code Playgroud)
在我看来,在我的调用中Ts应该推断出int(因为这是使第一个参数解决的唯一方法),然后其他一切都将被强制作为结果.为什么这不起作用?