下面的例子是一个很小的,也许不是一个众所周知的成语的例子.
它编译并且它是如此丑陋,以便能够保持最小,因为问题不是关于成语本身.
struct Foo {
virtual void fn() = 0;
};
template<class T>
struct Bar: public Foo {
void fn() override {
T{}.fn();
}
};
struct S {
void fn() { }
};
int main() {
Foo *foo = new Bar<S>{};
foo->fn();
}
Run Code Online (Sandbox Code Playgroud)
我一小时前就在苦苦思索的是如何改变它(甚至,如果存在替代习语),引入一个可变参数模板成员方法.
显然,我不能修改类的fn
功能Foo
,因为它是虚拟的,虚拟说明符不与模板一起使用.这对于fn
规范是有效的Bar
,因为它必须以某种方式覆盖基类中的那个.
注意.
因为我强烈怀疑这个问题可能是有史以来最伟大的XY问题之一,我还想简要介绍一下实际问题.
我有一个暴露两个模板化成员方法的类:
第一个接受一个T
不立即使用的模板类,而是应该以某种方式存储,以便以后使用.
第二个接受一个可变数量的参数(它实际上是一个可变参数的模板成员函数),这些参数应该完美地转发到新创建的实例T
.
嗯,问题要复杂得多,但这是一个很好的近似,应该让你知道目标是什么.
编辑
我猜它在某种程度上类似于高阶函数.
我的意思是,解决这个问题的确是一个模板化的函数来绑定第一个参数,但据我所知,这是不可能的,以及我迄今为止探索过的任何其他方法.
任何表达相同概念的可行解决方案?
再一次,在回答另一个问题时,我忘记了(我的错),只有在找到整数或浮点文字时才从声明集中选取文字运算符模板.
举个例子:
template <char... C>
constexpr int operator "" _x() {
return 0;
}
Run Code Online (Sandbox Code Playgroud)
它可以用作10_x
,但不能既不foo_x
用作"foo"_x也不能用.
除了明显的原因是因为标准说明了,在处理字符串文字时,他们不被考虑的技术原因(如果有的话)是什么?
我也找到了一个建议(好吧,不完全相同,但它提出了一个想法),但仍然不是一个选择.
什么阻止他们用于字符串?
不止一次(甚至在SO上)我见过这样的代码:
template<typename U, typename... G, typename T = Traits<U>>
struct {
static_assert(sizeof...(G) == 0, "!");
// ...
};
Run Code Online (Sandbox Code Playgroud)
或这个:
template<typename T, typename... G, typename = std::enable_if_t<condition<T>>
void func(T &&t) {
static_assert(sizeof...(G) == 0, "!");
// ....
}
Run Code Online (Sandbox Code Playgroud)
目的是避免用户做这样的事情来打破游戏规则:
template<typename T, typename = std::enable_if_t<std::is_same<T, int>>
void func(T &&t) {
// ....
}
// ...
func<int&, void>(my_int);
Run Code Online (Sandbox Code Playgroud)
使用guard参数包时,无法覆盖默认值.
另一方面,对尺寸的检查避免了无用参数的专业化污染.
无论如何,由于[temp.res/8],我们有:
该程序格式错误,无需诊断,如果:
[...]
- 可变参数模板的每个有效专业化都需要一个空模板参数包,或
[...]
因此,包含上述片段的程序是否格式错误?
请考虑以下代码:
auto f() -> decltype(int{0.}, void()) { }
int main() { f(); }
Run Code Online (Sandbox Code Playgroud)
它没有编译(如预期)并出现错误:
在{}内缩小'0.0'从'double'到'int'的转换
海湾合作委员会和克朗都同意这一点.
现在考虑以下代码:
template <typename T>
auto f(T) -> decltype(int{0.}, void()) { }
int main(){
f(0);
}
Run Code Online (Sandbox Code Playgroud)
在这种情况下,clang 3.9返回错误,GCC 6.2编译时没有错误.
在功能模板的情况下是否应该接受缩小转换或者是GCC的错误?
我打算向GCC打开一个问题,我想它应该无法编译,但我想知道我是否遗漏了一些关于函数模板的重要信息.
请考虑以下最小示例:
#include <functional>
#include <algorithm>
#include <list>
int main() {
std::list<std::function<void()>> list;
list.push_back([&list](){ list.push_back([](){ throw; }); });
std::for_each(list.cbegin(), list.cend(), [](auto &&f) { f(); });
}
Run Code Online (Sandbox Code Playgroud)
它在运行时编译并抛出异常.
我的猜测是只有第一个lambda被执行std::for_each
,但显然我错了:如果我在列表的末尾添加另一个lambda,迭代也会达到lambda.
让我们恢复示例(push_front
而不是push_back
和crbegin
/ crend
而不是cbegin
/ cend
):
#include <functional>
#include <algorithm>
#include <list>
int main() {
std::list<std::function<void()>> list;
list.push_front([&list](){ list.push_front([](){ throw; }); });
std::for_each(list.crbegin(), list.crend(), [](auto &&f) { f(); });
}
Run Code Online (Sandbox Code Playgroud)
由于前面的例子,我预计这也会编译和崩溃.
相反,它编译并且不会崩溃.这次,不执行推到列表前面的功能.
问题很简单:这是正确的吗?
为什么两个例子如此违反直觉?
在第一种情况下,我期待一些不同的东西,我错了,这不是问题.
无论如何,我希望两个循环之间的一致性.我的意思是,第二个函数在一个案例中执行,而在另一个案例中不执行,但我在两种情况下都是从开始到结束迭代.
我的推理出了什么问题?
请考虑以下代码:
int main() {
auto l = [](auto){};
void(*p)(int) = l;
}
Run Code Online (Sandbox Code Playgroud)
它与GCC和clang一起工作得很好.
让我们考虑以下略微修改的版本:
int main() {
auto l = [](auto...){};
void(*p)(int) = l;
}
Run Code Online (Sandbox Code Playgroud)
是否有任何理由拒绝此代码或是否是编译器的错误?
我打算提出一个问题,但我想知道是否有任何提案可以由其中一个提出,而不是由另一个提出.
我正在设计一个遍历多个容器的迭代器,因此将代理对象作为返回类型。因此,最好的办法是成为输入迭代器(这是因为前向迭代器需要reference
是实际的引用类型,而据我所知,这对于输入迭代器而言并非如此)。
(让我说)plain for_each
就像我的迭代器一样具有魅力。
但是,当我查看其并行版本时,我发现它仅接受转发迭代器。因此,我不能使用复杂的迭代器来返回代理对象,这很烦人。
另一方面,我在网上寻找其他著名的实现,这并不像我最初想象的那么普遍-例如,英特尔®TBB为每个接受输入迭代器的设备提供了自己的并行处理。
那么我的问题是:为什么并行不能std::for_each
与输入迭代器一起使用?
我看不到它们是正向迭代器的意义,因为乍一看它即使在输入迭代器中也能正常工作。我想念什么?
考虑以下情况:我有足够的存储空间来托管a void *
并因此就地构造一个指针。
是否可以保证相同的存储空间足够大,以便始终存储一个std::reference_wrapper
?
某种(出于我的想法而写,只是为了理解我的意思):
std::aligned_storage_t<sizeof(void *), alignof(void *)> storage;
// ...
int value = 0;
new (&storage) std::reference_wrapper<int>{value};
Run Code Online (Sandbox Code Playgroud)
通过快速而肮脏的测试,我发现在我的机器上这是有效的,即与的大小std::reference_wrapper
相符void *
。但是,在不同平台上可能是这种情况。同时,我在标准中找不到关于和的大小的任何线索,std::reference_wrapper
而且我想知道它的实现是否已定义或是否有任何保证。
为了提供上下文,我正在围绕不同类型(类似于std::any
)的不透明包装器进行包装,该包装器执行小型对象优化以避免可能的分配。
收到时std::reference_wrapper
,我想使用与用于区别sizeof(T) > sizeof(void *)
其他情况的路径不同的路径。但是,我不知道我是否可以就地复制包装器的构造,或者在这种情况下是否还应该依靠分配。
我有以下问题:
#include <vector>
#include <tuple>
using namespace std;
template< size_t... N_i, typename Ts... >
class A
{
// ...
private:
std::vector<size_t> _v = { N_i... };
std::tuple<Ts...> _t;
};
int main()
{
A<1> a;
}
Run Code Online (Sandbox Code Playgroud)
如您所见,我尝试将多个参数包定义为类的模板参数A
.
不幸的是,代码无法编译:
错误:'Ts'之前的预期嵌套名称说明符
如何为此示例定义多个参数包?
如何为传递给模板的任何函数获取返回类型?
我不知道如何转换template<typename T>
和template<typename Result, typename Args...>
:
template<typename T>
void print_name(T f)
{
static_assert(internal::is_function_pointer<T>::value
|| std::is_member_function_pointer<T>::value,
"T must be function or member function pointer.");
typename decltype(f(...)) Result; // ???
typename std::result_of<T>()::type Result; // ???
printf("%s\n", typeid(Result).name());
}
void f_void() {}
int f_int(int x) { return 0; }
float f_float(int x, int y) { return 0.f; }
struct X { int f(int x, float y) { return 0; } };
int main()
{
print_name(f_void);
print_name(f_int);
print_name(f_float);
print_name(&X::f);
return …
Run Code Online (Sandbox Code Playgroud)