考虑以下程序。
#include <iostream>
template <typename T>
void f( void ( *fn )( T ) )
{
fn( 42 );
}
void g( int x )
{
std::cout << "g( " << x << " );\n";
}
int main()
{
f( g );
}
Run Code Online (Sandbox Code Playgroud)
程序编译成功,输出为
g( 42 );
Run Code Online (Sandbox Code Playgroud)
现在让我们将非模板函数重命名g为f.
#include <iostream>
template <typename T>
void f( void ( *fn )( T ) )
{
fn( 42 );
}
void f( int x )
{
std::cout << "f( …Run Code Online (Sandbox Code Playgroud) c++ templates language-lawyer template-argument-deduction c++17
我正在寻找方法从编译器的逻辑中获取一些跟踪日志,当它试图推断模板参数类型时,无论何时成功.例如,给出代码:
#include <iostream>
#include <vector>
#include <type_traits>
template<typename T>
decltype(auto) foo(T&& t) -> decltype(t + t)
{
return t + t;
}
template<typename T>
decltype(auto) foo(T&& t) -> decltype(t.size())
{
return t.size();
}
int main()
{
std::cout << foo(10) << '\n'
<< foo(std::vector<int>{1,2,3}) << '\n';
}
Run Code Online (Sandbox Code Playgroud)
我很想得到类似的东西:
foo(10)
candidate: decltype(auto) foo(T&& t) -> decltype(t * t): seems valid
candidate: decltype(auto) foo(T&& t) -> decltype(t.size()): wrong one
Run Code Online (Sandbox Code Playgroud)
编译器已经非常擅长,例如提供不明确的调用.例如,如果我打电话给foo(std::string("qwe"));我:
main.cpp: In function 'int main()':
main.cpp:23:31: error: call of overloaded 'foo(std::__cxx11::basic_string<char>)' …Run Code Online (Sandbox Code Playgroud) 让代码首先用天真的方法说话:
int heavy_calc() // needed to be called once
{
// sleep(7500000 years)
return 42;
}
int main()
{
auto foo = [] {
// And cached for lambda return value
static int cache = heavy_calc();
return cache;
};
return foo() + foo();
}
Run Code Online (Sandbox Code Playgroud)
我希望在第一次调用时计算lambda内部缓存值.一种天真的方法是使用static缓存,但它会增加二进制大小并拒绝内联.
我想出了建立cache在捕获列表和标记lambda作为mutable,有什么不内联的问题,但需要缓存开始使用默认值,这可能会破坏类不变.
auto foo = [cache=0] () mutable {
// And cached for lambda return value
if(!cache)
cache = heavy_calc();
return cache;
};
Run Code Online (Sandbox Code Playgroud)
我的第三种方法用于boost::optional可变 …
这是理论上的一个,但是我的问题是,如果lambda是在模板函数内部定义的,但不依赖于类型T也没有捕获任何东西(因此技术上可以在模板之外声明)是由编译器识别和优化的吗?通过优化我的意思是两个foo<int>和foo<double>将使用相同的二进制代码lam,因此它不重复.也许,他们是否需要按标准执行此操作?我试图分析这个,但我想出的唯一想法是在里面尝试静态变量,但不同类型的lambdas不共享相同的静态变量并不奇迹.
// foo with helper lam which is captureless and not T-dependant
template<typename T>
int foo(T x)
{
auto lam = [](int x) {std::cout << "foo" << std::endl; return -x; };
return lam(sizeof(x));
}
// foo with helper lam which is captureless and not T-dependant with static inside
template<typename T>
int foo_s(T x)
{
auto lam = [](int x) {static int count = 0; std::cout << "foo_s " << count++ << std::endl; return -x; …Run Code Online (Sandbox Code Playgroud) 这是最近出现的事情,我觉得它显然不应该起作用:
#include <iostream>
#include <memory>
int main()
{
std::shared_ptr<int>& ptr = const_cast<std::shared_ptr<int>&>(
static_cast<const std::shared_ptr<int>&>(
std::shared_ptr<int>(
new int(5), [](int* p) {std::cout << "Deleting!"; *p = 999; delete(p); }
)
)
);
std::cout << "I'm using a non-const ref to a temp! " << *ptr << " ";
}
Run Code Online (Sandbox Code Playgroud)
shared_ptr此处不需要使用,但自定义删除器允许轻松演示结果对象的生命周期。Visual Studio、Clang 和 GCC 的结果输出是相同的:
我正在使用非常量引用来表示临时值!5 删除!
这意味着结果的生命周期shared_ptr已通过某种机制扩展到匹配std::shared_ptr<int>& ptr.
现在,我知道在常量引用的情况下,临时对象的生命周期将扩展到引用的生命周期。但是唯一的命名对象是非常量引用,我希望所有其他中间表示的生命周期仅等于初始化表达式。
此外,Microsoft 有一个扩展,它允许非常量引用延长绑定临时的生命周期,但即使禁用该扩展,这种行为似乎也存在,此外,也出现在 Clang 和 GCC 中。
根据这个答案,我相信临时对象被隐式创建为const,因此尝试修改引用的对象ptr可能是未定义的行为,但我不确定这些知识是否告诉我有关为什么延长生命周期的任何信息。我的理解是,这是修改UB 的 const的行为,而不是简单地对它进行非常量引用。 …
c++ ×5
c++14 ×2
c++17 ×2
lambda ×2
c++11 ×1
const-cast ×1
template-argument-deduction ×1
templates ×1