是否可以即时声明一个新类型(一个空的struct或没有实现的struct)?
例如
constexpr auto make_new_type() -> ???;
using A = decltype(make_new_type());
using B = decltype(make_new_type());
using C = decltype(make_new_type());
static_assert(!std::is_same<A, B>::value, "");
static_assert(!std::is_same<B, C>::value, "");
static_assert(!std::is_same<A, C>::value, "");
Run Code Online (Sandbox Code Playgroud)
一个“手动”解决方案是
template <class> struct Tag;
using A = Tag<struct TagA>;
using B = Tag<struct TagB>;
using C = Tag<struct TagC>;
Run Code Online (Sandbox Code Playgroud)
甚至
struct A;
struct B;
struct C;
Run Code Online (Sandbox Code Playgroud)
但是对于模板/ meta,一些魔术make_new_type()功能会很好。
有状态的元编程格式不正确,这样的事情是否可能呢?
c++ templates metaprogramming stateful compile-time-constant
我很好奇是否有可能在编译时确保在一个地方调用一个方法。
请注意,如果函数被调用多次(例如在一个循环中)是可以的 - 但它不应该在两个单独的循环中调用。
这可以分为两部分,我也对涵盖任一部分的解决方案感兴趣:
(a) 确保至少在一个地方
调用一个方法 (b) 确保一个方法最多在一个地方调用
我可以完全控制代码的结构,欢迎使用实现相同想法的不同习语。
// class.h
class MyClass {
public:
void my_method();
}
Run Code Online (Sandbox Code Playgroud)
以下不应编译(从未调用)
#include "class.h"
int main() {
MyClass my_class;
}
Run Code Online (Sandbox Code Playgroud)
以下不应编译(在多个地方调用)
#include "class.h"
int main() {
MyClass my_class;
my_class.my_method();
while(true) {
my_class.my_method();
}
}
Run Code Online (Sandbox Code Playgroud)
以下应该编译(在一个地方调用):
#include "class.h"
int main() {
MyClass my_class;
while(true) {
my_class.my_method();
}
}
Run Code Online (Sandbox Code Playgroud) 请考虑以下实现编译时计数器的代码.
#include <iostream>
template<int>
struct Flag { friend constexpr int flag(Flag); };
template<int N>
struct Writer
{
friend constexpr int flag(Flag<N>) { return 0; }
};
template<int N>
constexpr int reader(float, Flag<N>) { return N; }
template<int N, int = flag(Flag<N>{})>
constexpr int reader(int, Flag<N>, int value = reader(0, Flag<N + 1>{}))
{
return value;
}
template<int N = reader(0, Flag<0>{}), int = sizeof(Writer<N>) >
constexpr int next() { return N; }
int main() {
constexpr int a = next(); …Run Code Online (Sandbox Code Playgroud) 另一个问题是"g ++和clang ++之间谁是对的?" 对于C++标准大师.
以下代码
template <int>
struct foo
{
template <typename>
friend void bar ()
{ }
};
int main ()
{
foo<0> f0;
foo<1> f1;
}
Run Code Online (Sandbox Code Playgroud)
使用clang ++编译没有问题(只有两个"未使用的变量"警告)但是给出以下错误
tmp_002-11,14,gcc,clang.cpp: In instantiation of ‘struct foo<1>’:
tmp_002-11,14,gcc,clang.cpp:27:12: required from here
tmp_002-11,14,gcc,clang.cpp:20:16: error: redefinition of ‘template<class> void bar()’
friend void bar ()
^~~
tmp_002-11,14,gcc,clang.cpp:20:16: note: ‘template<class> void bar()’ previously defined here
Run Code Online (Sandbox Code Playgroud)
用g ++编译.
像往常一样,问题是:谁是对的?g ++或clang ++?
在我的Debian平台上查看了clang ++ 3.9.1和g ++ 6.3.0.但是,尝试使用Wandbox,似乎与更新的版本相同.
根据C ++ 17标准[temp.point] / 4,重点是
对于类模板专业化,类成员模板专业化或对类模板的类成员的专业化,如果该专业化是隐式实例化的,因为该专业化是从另一个模板专业化内部引用的,则如果引用该专业化的上下文取决于在模板参数上,并且如果未在封闭模板的实例化之前实例化专门化,则实例化点紧接在封闭模板的实例化点之前。否则,此类专门化的实例化点紧接在引用该专门化的名称空间范围声明或定义之前。
我不懂如何解释这句话。在其他地方,当描述模板中的名称查找时,该标准指的是“实例上下文”和“定义上下文”。此处,“上下文”一词似乎表示源文本中的特定点,它决定了可见名称的集合。在这里,我不确定“上下文”的含义是否相同。如果那是什么意思,我不确定依赖模板参数对它意味着什么。这是否意味着它在模板内部,还是具体意味着在编译器决定实例化所讨论的专业化时,封闭模板中仍然存在一些未绑定的模板参数?
例子将不胜感激。
我正在尝试学习一些神秘的有状态模板元编程技巧。
(这就是我想学习它的原因。不幸的是,这个库在 GCC 8 和 Clang 上都不起作用。)
我需要的第一个明显的东西是一个constexpr计数器:
/*something*/ constexpr int foo() /*something*/
int main()
{
constexpr int a = foo();
constexpr int b = foo();
constexpr int c = foo();
static_assert(a == 0 && b == 1 && c == 2);
}
Run Code Online (Sandbox Code Playgroud)
最好它应该是一个标记的计数器,以便我可以同时拥有多个计数器:
/*something*/ constexpr int foo() /*something*/
struct TagA {};
struct TagB {};
int main()
{
constexpr int a = foo<TagA>();
constexpr int b = foo<TagA>();
constexpr int c = foo<TagA>();
constexpr …Run Code Online (Sandbox Code Playgroud) 为了直观的展示问题,可以直接看'UPDATE'部分
#include <iostream>
template<int N>
struct state
{
static constexpr int value = N;
friend auto create(state<N>);
};
template<int N>
struct generate_state
{
friend auto create(state<N>) {
return state<N>{};
}
static constexpr int value = N;
};
template struct generate_state<1>;
template<int N, typename U = decltype(create(state<N - 1>{})) >
std::size_t getvalue(float,state<N>,int res = generate_state<N>::value) { #1
return N;
}
template<int N, typename U = decltype(create(state<N>{})) >
std::size_t getvalue(int, state<N>, int r = getvalue(0, state<N + 1>{})) { #2
return N; …Run Code Online (Sandbox Code Playgroud) 我将从我想象如何使用我想创建的代码开始。它不必完全像这样,但它是我在标题中所说的“简洁”的一个很好的例子。就我而言,它是将类型映射到相关枚举值。
struct bar : foo<bar, foo_type::bar> { /* ... */ };
// \_/ \___________/
// ^ Type ^ Value
Run Code Online (Sandbox Code Playgroud)
理想情况下,这应该做的是自动注册foo一个类型的第一个模板参数和第二个值之间的双向映射,只需使用继承语法和正确的模板参数,以便我稍后可以执行下面示例中的操作.
foo_type value = to_value<bar>; // Should be foo_type::bar
using type = to_type<foo_type::bar>; // Should be bar
Run Code Online (Sandbox Code Playgroud)
我知道我可以为每个类型值对手动编写两个模板特化来做到这一点,但我想知道它是否会比不使用宏更乏味。
我已经尝试过的是......
struct foo_base
{
template<typename T>
struct to_value
{};
template<foo_type E>
struct to_type
{};
};
template<typename T, foo_type E>
struct foo : public foo_base
{
template<>
struct to_value<T>
{
static constexpr auto value = E; …Run Code Online (Sandbox Code Playgroud) 我遇到过以下代码,其行为得到了 GCC、Clang 和 MSVC 的一致认可:
#include <concepts>
template<typename T>
auto foo(T);
template<typename U>
struct S {
template<typename T>
friend auto foo(T) {
return U{};
}
};
S<double> s;
static_assert(std::same_as<decltype(foo(42)), double>);
Run Code Online (Sandbox Code Playgroud)
现场演示: https: //godbolt.org/z/hK6xhesKM
foo()在全局命名空间中声明并推导返回类型。S<U>提供了 via 友元函数的定义foo(),它返回类型为 的值U。
我期望的是,当S用 实例化时U=double,它的定义foo()被放入全局命名空间中U并被替换,由于友元函数的工作方式,有效地如下所示:
template<typename T>
auto foo(T);
S<double> s;
template<typename T>
auto foo(T) {
return double{};
}
Run Code Online (Sandbox Code Playgroud)
因此我期望foo()的返回类型是double,并且以下静态断言应该通过:
static_assert(std::same_as<decltype(foo(42)), double>);
Run Code Online (Sandbox Code Playgroud)
然而,实际发生的情况是,所有三个编译器对此行为都持不同意见。
正如我所期望的,GCC 通过了静态断言。
Clang 失败了静态断言,因为它似乎相信 …
我正在寻找一种在编译时动态构建类型列表的方法.我正在创建包含不同类型的组件,并且我想构建一个类型列表,其中包含注册为组件的所有类型.想象一下:
using Name = Component<std::string>;
using Position = Component<Vec2f>;
Run Code Online (Sandbox Code Playgroud)
那么我希望能够与持有型列表落得std::string和Vec2f在这种情况下,我可以访问和扩展,即是这样的:
using AllComponents = SomeMagicTypeThatHoldsAllComponents;
Run Code Online (Sandbox Code Playgroud)
我已经构建了类型列表部分,它的工作原理如下:
using List = typename MakeTypeList<int, double, std::string>::List;
Run Code Online (Sandbox Code Playgroud)
您可以将类型追加或添加到List这样:
using Append = typename AppendType<List, float>::List;
using Prepend = typename PrependType<List, Vec2f>::List;
Run Code Online (Sandbox Code Playgroud)
并按照这样的索引获取类型(这基本上允许您在帮助下扩展类型std::integer_sequence):
using FirstType = typename TypeAt<List, 0>::Type;
Run Code Online (Sandbox Code Playgroud)
我无法解决的问题是如何在后台动态构建此列表而不破坏简单的组件API.有人建议是否或如何实现这一目标?
编辑: 要澄清,我现在能想出的唯一解决方案是将类型列表作为额外的模板参数传递,即
using AllTypes = using MakeTypeList<>::List;
using Name = Component<AllTypes, std::string>;
using Position = Component<AllTypes, Vec2f>;
Run Code Online (Sandbox Code Playgroud)
我感兴趣的是一个解决方案,我不必通过类型列表.
谢谢!
所以我这里有一些使用 gcc、clang 和 msvc 编译的代码:
#include <cstdio>
#include <type_traits>
struct c_class;
template <class T> struct holder { friend auto adl_lookup(holder<T>); };
template <class C, class T> struct lookup {
friend auto adl_lookup(holder<T>) { return holder<C>{}; }
};
struct cpp_class : lookup<cpp_class, c_class *> {
cpp_class() {}
};
int main() {
static_assert(std::is_same<holder<cpp_class>,
decltype(adl_lookup(holder<c_class *>{}))>{},
"Failed");
}
Run Code Online (Sandbox Code Playgroud)
之所以在类adl_lookup中定义lookup而不是在类中定义,是为了在继承CRTP类时holder可以从c_class到进行“反向”查找。所以友元函数不能移到类中。cpp_classlookup<cpp_class, c_class *>holder
但是,在 gcc 上,我收到有关非模板友元函数的警告:
<source>:9:37: warning: friend declaration 'auto adl_lookup(holder<T>)' declares a …Run Code Online (Sandbox Code Playgroud) 请考虑以下代码:
template <class /* Weird Hack Here */>
struct object
{
constexpr object(int value, /* Other Weird Hack */) noexcept;
};
int main()
{
object foo(1);
object bar(1);
}
Run Code Online (Sandbox Code Playgroud)
是否有可能在C++ 17中使用奇怪的技巧来拥有foo和bar成为不同的类型?