以下代码在C++ 98,C++ 11和C++ 14模式下对我尝试的所有GCC版本产生后续编译错误:
struct T
{
T(void* x) : (x) {}
};
// main.cpp: In constructor 'T::T(void*)':
// main.cpp:3:18: error: anachronistic old-style base class initializer [-fpermissive]
// T(void* x) : (x) {}
// ^
// main.cpp:3:16: error: unnamed initializer for 'T', which has no base classes
// T(void* x) : (x) {}
Run Code Online (Sandbox Code Playgroud)
当然,它显然是破碎的代码,因为我实际上并没有初始化任何东西.
但为什么它是基类初始化器,为什么它是"不合时宜的",而不是简单的错误?曾经有效吗?什么时候?这是什么意思?
我在网上找到的唯一的相关参考文献是,当一个成员名称被意外地宏出时,人们遇到了错误,实际上产生了与上面相同的代码:
#define bar
// ^ some library could have done this
struct T
{
T(int x)
: bar(x) // effectively just `: …Run Code Online (Sandbox Code Playgroud) 根据std :: shufle上的cppreference.com参考站点,在c ++ 14中不推荐使用以下方法:
template< class RandomIt >
void random_shuffle( RandomIt first, RandomIt last );
Run Code Online (Sandbox Code Playgroud)
为什么我们不再能够在不传递第三个参数的情况下调用以下函数?
std::random_shuffle(v.begin(),v.end()); //no longer valid in c++14
Run Code Online (Sandbox Code Playgroud)
看起来好像不同的功能减速具有默认参数设置.这背后的原因是什么?是否添加了某种替代方案?
我什么时候应该声明我的功能:
void foo(Widget w);
而不是
void foo(Widget&& w);?
假设这是唯一的重载(例如,我选择一个或另一个,不是两个,也没有其他重载).没有涉及模板.假设该函数foo需要所有权Widget(例如const Widget&,不是本讨论的一部分).我对这些情况范围之外的任何答案都不感兴趣.请参阅帖子末尾的附录,了解为什么这些约束是问题的一部分.
我和我的同事可以提出的主要区别是rvalue参考参数强制您明确复制副本.调用者负责制作一个显式副本,然后在std::move需要副本时将其传入.在按值传递的情况下,隐藏了副本的成本:
//If foo is a pass by value function, calling + making a copy:
Widget x{};
foo(x); //Implicit copy
//Not shown: continues to use x locally
//If foo is a pass by rvalue reference function, calling + making a copy:
Widget x{};
//foo(x); //This would be a compiler error
auto copy = x; //Explicit copy
foo(std::move(copy));
//Not shown: continues …Run Code Online (Sandbox Code Playgroud) 我尝试实现C++ 14别名模板make_integer_sequence,这简化了类模板的创建integer_sequence.
template< class T, T... I> struct integer_sequence
{
typedef T value_type;
static constexpr size_t size() noexcept { return sizeof...(I) ; }
};
template< class T, T N>
using make_integer_sequence = integer_sequence< T, 0,1,2, ... ,N-1 >; // only for illustration.
Run Code Online (Sandbox Code Playgroud)
为了实现,make_integer_sequence我们需要一个辅助结构make_helper.
template< class T , class N >
using make_integer_sequence = typename make_helper<T,N>::type;
Run Code Online (Sandbox Code Playgroud)
实施make_helper并不太难.
template< class T, T N, T... I >
struct make_helper
{
typedef …Run Code Online (Sandbox Code Playgroud) C++ 98有front_inserter,back_inserter和inserter,但在C++ 11或草案C++ 14中似乎没有任何这些版本.有没有我们不能有任何技术原因front_emplacer,back_emplacer和emplacer?
草案C++ 14包括运行时大小的数组和std::dynarray容器.从我所知道的,两者之间的唯一真正的区别在于std::dynarray有一个STL接口(例如begin,end,size,等),同时运行时大小的数组没有.那么为什么C++ 14需要它们呢?
我知道运行时大小的数组是核心语言的std::dynarray一部分,同时也是标准库的一部分,但是该提议std::dynarray清楚地表明作者希望编译器在许多情况下能够提供特殊支持,std::dynarray以便它可以高效尽可能地,即,与运行时大小的数组一样高效.因此,语言/库的区别似乎有点人为.
那么,为什么C++ 14需要两个运行时大小的数组std::dynarray呢?并且鉴于它std::dynarray具有更丰富的(STLified)接口,为什么不只是删除运行时大小的数组,假设std::dynarray可以以相同的运行时效率实现?
澄清
当我谈到"运行时大小的数组"时,我指的是N3639中描述的新的C++ 14核心语言特性,而不是传统的C数组或VLA或C++ 11中的任何内容.
继我对这个问题的回答之后,在C++ 11和C++ 14中:
[C++11, C++14: 25.5/2]:内容与标准C库头相同<stdlib.h>,但以下情况除外:
[C++11, C++14: 25.5/3]:功能签名:Run Code Online (Sandbox Code Playgroud)bsearch(const void *, const void *, size_t, size_t, int (*)(const void *, const void *));被两个声明取代:
Run Code Online (Sandbox Code Playgroud)extern "C" void *bsearch(const void *key, const void *base, size_t nmemb, size_t size, int (*compar)(const void *, const void *)); extern "C++" void *bsearch(const void *key, const void *base, size_t nmemb, size_t size, int (*compar)(const void *, const void *));两者都具有与原始声明相同的行为.
然而,
[C++11, C++14: 7.5/5]:如果两个声明声明具有相同名称的函数,并且参数类型列表(8.3.5)是同一命名空间的成员,或者声明具有相同名称的对象是同一命名空间的成员,并且声明为这些名称提供不同的语言链接,该计划是不正常的; 如果声明出现在不同的翻译单元中,则无需诊断.[..]
这是一个缺陷吗?
在C++ 11中编写递归lambda函数有一个经常重复的"技巧",如下所示:
std::function<int(int)> factorial;
factorial = [&factorial](int n)
{ return n < 2 ? 1 : n * factorial(n - 1); };
assert( factorial(5) == 120 );
Run Code Online (Sandbox Code Playgroud)
(例如,C++ 0x中的递归lambda函数.)
这种技术有两个直接的缺点:std::function<Sig>对象的目标(通过引用捕获)与非常特定的std::function<Sig>对象(此处factorial)相关联.这意味着通常无法从函数返回生成的仿函数,否则引用将保持悬空状态.
另一个(尽管不那么直接)问题是使用std::function通常会阻止编译器优化,这是在其实现中需要类型擦除的副作用.这不是假设,可以很容易地进行测试.
在假设的情况下,递归lambda表达式真的很方便,有没有办法解决这些问题?
考虑以下代码片段:
bool foo(const std::string& s) {
return s == "hello"; // comparing against a const char* literal
}
bool bar(const std::string& s) {
return s == "hello"s; // comparing against a std::string literal
}
Run Code Online (Sandbox Code Playgroud)
在乍一看,它看起来像比对并const char*需要更少的组装说明1,作为使用字符串字面量会导致就地建设std::string。
(编辑:正如答案中指出的那样,我忘记了有效地s.compare(const char*)将被调用的事实foo(),因此在这种情况下当然不会进行就地构建。因此,请在下面删除一些行。)
但是,请operator==(const char*, const std::string&)参阅参考资料:
所有比较都是通过
compare()成员函数完成的。
根据我的理解,这意味着我们将需要构造一个结构 std::string来执行比较,因此我怀疑最终的开销将是相同的(尽管对的调用已将其隐藏了operator==)。
1我知道更少的汇编指令并不一定意味着更快的代码,但是我不想在这里进行微基准测试。
的实现std::swap可能是这样的:
template <class T> void swap (T& a, T& b)
{
T c(std::move(a)); a=std::move(b); b=std::move(c);
}
template <class T, size_t N> void swap (T (&a)[N], T (&b)[N])
{
for (size_t i = 0; i<N; ++i) swap (a[i],b[i]);
}
Run Code Online (Sandbox Code Playgroud)
实现std::exchangen3668可能如下所示:
template< typename T, typename U = T >
T exchange( T & obj, U && new_val )
{
T old_val = std::move(obj);
obj = std::forward<U>(new_val);
return old_val;
}
Run Code Online (Sandbox Code Playgroud)
它说:
对于原始类型,这相当于明显的实现,而对于更复杂的类型,这个定义
- 当该类型定义移动构造函数时,避免复制旧值
- 接受任何类型作为新值,利用任何转换赋值运算符
- 如果它是临时的或移动的,则避免复制新值.
我选择了与atomic_exchange对称的名称,因为它们的行为相同,除了这个函数不是原子的.
n3746还提出了一个内置的交换运算符,如下所示: …