使用c++ 11类型特征来提供替代的内联实现
Using C++11 type traits to provide alternate inline implementations
当在模板化代码中使用特征时,以下代码模式是否合理,其中两个替代实现总是可编译的?
阅读代码似乎比做其他有条件编译的把戏更清楚(但可能我只是不太熟悉那些把戏)。
template<typename T>
class X
{
void do_something() noexcept(std::is_nothrow_copy_constructible<T>::value)
{
if (std::is_nothrow_copy_constructible<T>::value)
{
// some short code that assumes T's copy constructor won't throw
}
else
{
// some longer code with try/catch blocks and more complexity
}
}
// many other methods
};
(增加的复杂性部分是为了提供强异常保证。)
我知道这段代码将工作,但是期望编译器消除constant-false分支并为noexcept情况(比其他情况简单得多)进行内联等是合理的吗?我希望在noexcept的情况下,像只写第一个块作为主体的方法一样有效(反之亦然,尽管我不太担心复杂的情况)。
如果这不是正确的方法,谁能告诉我推荐的语法?
[…期望编译器消除constant-false分支并为noexcept情况(比其他情况简单得多)进行内联等是合理的吗?
有可能,但我不相信,因为你无法控制它。
如果要删除if/else
,可以指定返回类型并清除noexcept
限定符。
例如:
template<typename T>
class X {
template<typename U = T>
std::enable_if_t<std::is_nothrow_copy_constructible<T>::value>
do_something()
noexcept(true)
{}
template<typename U = T>
std::enable_if_t<not std::is_nothrow_copy_constructible<T>::value>
do_something()
noexcept(false)
{}
};
缺点是现在有两个成员函数模板。
我不确定它是否符合你的要求。
如果你被允许使用c++ 17的特性,if constexpr
可能是你要走的路,你不必再在两个成员函数中破坏你的方法。
另一种方法可以基于标记调度类型的noexcept
属性。例如:
template<typename T>
class X {
void do_something(std::true_type)
noexcept(true)
{}
void do_something(std::false_type)
noexcept(false)
{}
void do_something()
noexcept(do_something(std::is_nothrow_copy_constructible<T>{}))
{ do_something(std::is_nothrow_copy_constructible<T>{}); }
};
我知道,sfinae不是动词,但是to sfinae something听起来太好了。
期望编译器消除constant-false分支并为noexcept情况做内联等是否合理[…]?
是的。也就是说,必须实例化constant-false分支,这可能会或可能不会导致编译器实例化一堆你不需要的符号(然后你需要依赖链接器来删除它们)。
我仍然会使用SFINAE的诡计(实际上是标签调度),这在c++ 11中非常容易完成。
template<typename T>
class X
{
void do_something() noexcept(std::is_nothrow_copy_constructible<T>::value)
{
do_something_impl(std::is_nothrow_copy_constructible<T>() );
}
void do_something_impl( std::true_type /* nothrow_copy_constructible */ )
{
// some short code that assumes T's copy constructor won't throw
}
void do_something_impl( std::false_type /* nothrow_copy_constructible */)
{
// some longer code with try/catch blocks and more complexity
}
// many other methods
};
如果你要在所有其他方法中检查nothrow_copy_constructor
,你可以考虑专门化整个类:
template<typename T, class = std::is_nothrow_copy_constructible_t<T> >
class X
{
//throw copy-ctor implementation
};
template<typename T>
class X<T, std::true_type>
{
// noexcept copy-ctor implementation
};
期望编译器消除constant-false分支是否合理?
是的,消除死代码是最简单的优化之一。
…在noexcept情况下做内联等吗?
我的第一个冲动是回答"不,你不能依赖它,因为它取决于内联传递相对于死代码消除步骤在优化流程中的位置"。
但是经过更多的思考,我不明白为什么一个成熟的编译器在足够高的优化水平上不会在内联步骤之前和之后执行死代码消除。所以这个期望也应该是合理的。
然而,猜测关于优化从来都不是一件确定的事情。追求简单的实现并得到功能正确的代码。然后衡量它的表现,并检查你的假设是否正确。如果没有,那么根据您的情况重新设计实现并不会比从一开始就沿着保证的路径花费更多的时间。
每个成熟的编译器都会消除死代码。每个成熟的编译器都会检测到常数分支,并对另一个分支进行死编码。
您可以创建一个带有十几个模板参数的函数,它在函数体中使用朴素的if
检查,并假设地查看结果——这不会有问题。
如果您创建static
变量或thread_local
或实例化符号,这些都很难消除。
内联有点棘手,因为编译器倾向于在某些时候放弃内联;代码越复杂,编译器就越有可能在内联之前放弃。
在c++ 17中,您可以将if
升级到constexpr
版本。但是在c++ 14和c++ 11中,你的代码可以做得很好。它比备选方案更简单,更容易阅读。
有点脆弱,但是如果它在编译时被破坏了,它通常会以一种嘈杂的方式发生。
但是期望编译器消除constant-false分支
是否合理?
。编译器将计算所有分支。您可以尝试使用c++17中的if constexpr
。
你想要实现的是SFINAE
您可以尝试自己实现constexpr_if
。c++11的解决方案如下:
#include <iostream>
#include <type_traits>
template <bool V>
struct constexpr_if {
template <class Lambda, class... Args>
static int then(Lambda lambda, Args... args) {
return 0;
}
};
template <>
struct constexpr_if<true> {
template <class Lambda, class... Args>
static auto then(Lambda lambda, Args... args) -> decltype(lambda(args...)) {
return lambda(args...);
}
static int then(...) {
return 0;
}
};
struct B {
B() {}
B(const B &) noexcept {}
void do_something() {
std::cout << "B::do_something()" << std::endl;
}
};
struct C {
C() {}
C(const C &) noexcept {}
void do_something_else() {
std::cout << "C::do_something_else()" << std::endl;
}
};
struct D {
D() {}
D(const D &) throw(int) {}
void do_something_else() {
std::cout << "D::do_something_else()" << std::endl;
}
};
template <class T>
struct X {
void do_something() {
T t;
constexpr_if<std::is_nothrow_copy_constructible<T>::value>::then([](B &b) {
b.do_something();
}, t);
constexpr_if<std::is_nothrow_copy_constructible<T>::value>::then([](C &c) {
c.do_something_else();
}, t);
constexpr_if<!std::is_nothrow_copy_constructible<T>::value>::then([](D &d) {
d.do_something_else();
}, t);
}
};
int main() {
X<B> x;
x.do_something();
X<C> xx;
xx.do_something();
X<D> xxx;
xxx.do_something();
}
输出:B::do_something() C::do_something_else() D::do_something_else()
- 如果没有malloc,链表实现将失败
- 如何在c++中实现处理器调度模拟器
- 如何在c++中使用引用实现类似python的行为
- 实现无开销push_back的最佳方法是什么
- 使用简单类型列表实现的指数编译时间.为什么
- 如何在BST的这个简单递归实现中消除警告
- 实现一个在集合上迭代的模板函数
- 我应该实现右值推送功能吗?我应该使用std::move吗
- 如何正确实现和访问运算符的各种自定义枚举器
- C++Union/Struct位域的实现和可移植性
- 这个极客对极客的trie实现是否存在内存泄漏问题
- 在c++中实现LinkedList时,应出现未处理的错误
- 为左值和右值的包装器实现C++范围
- 使用模板进行堆栈实现; "name followed by :: must be a class or namespace"
- 使用GSoap实现ONVIF
- 在用于格式4的arm模拟器中实现功能时的一个问题
- 用于AVX的ln(x)的实现,m256
- 用常见虚拟函数实现的任意组合来实现派生类的正确方法是什么
- 在C++中,如何在类和函数(可能是模板化的)的头中编写完整的实现
- std::random_device是如何实现的