标签: c++-templates

您可以使用花括号初始化列表作为(默认)模板参数吗?

我需要定义一个接受多个 3D 坐标作为参数的 C++ 模板。当这些坐标的所有维度都定义为单独的整数变量时,参数列表将变得非常长 - 3 个坐标需要 9 个参数,这使得模板难以使用。

因此,非常希望以使用编译时数组的方式声明模板。它们的默认参数也应该直接在模板声明的位置声明为值,而不是变量名。

经过一些实验,令我惊讶的是,我发现 GCC 13 将接受以下 C++ 程序std=c++20

#include <cstdio>
#include <array>

template <
    std::array<int, 3> offset = {0, 0, 0}
>
struct Array
{
    void operator() (int i, int j, int k) 
    {
        printf("(%d, %d, %d)\n", i + offset[0], j + offset[1], k + offset[2]);
    }
};

int main(void)
{
    Array arr_default;
    arr_default(0, 0, 0);

    Array<{1, 1, 1}> arr;
    arr(0, 0, 0);

    return 0;
}
Run Code Online (Sandbox Code Playgroud)

然而,clang 18 拒绝使用 …

c++ language-lawyer list-initialization braced-init-list c++-templates

15
推荐指数
1
解决办法
660
查看次数

使用 Clang 编译时链接器抱怨未定义的引用

我遇到了一个非常奇怪的 clang 问题。

当我使用 gcc-11 使用 C++20 编译代码时,一切都很好。

当我尝试使用 C++20 和 clang-14 编译它时出现问题(使用 clang-15/16/17 没有帮助)。

链接器抱怨对容器的所有类模板实例化的未定义引用,下面是错误消息之一:

/opt/compiler-explorer/gcc-11.2.0/lib/gcc/x86_64-linux-gnu/11.2.0/../../../../x86_64-linux-gnu/bin/ld: /tmp/example-ded8d6.o:(.rodata._ZTV6WidgetINSt7__cxx114listIlSaIlEEEE[_ZTV6WidgetINSt7__cxx114listIlSaIlEEEE]+0x10): undefined reference to `Widget<std::__cxx11::list<long, std::allocator<long> > >::doSth()'
/opt/compiler-explorer/gcc-11.2.0/lib/gcc/x86_64-linux-gnu/11.2.0/../../../../x86_64-linux-gnu/bin/ld: /tmp/example-ded8d6.o:(.rodata._ZTV6WidgetISt6vectorIbSaIbEEE[_ZTV6WidgetISt6vectorIbSaIbEEE]+0x10): undefined reference to `Widget<std::vector<bool, std::allocator<bool> > >::doSth()'
Run Code Online (Sandbox Code Playgroud)

到目前为止,我已经设法通过更改 Widget 的 doSth 签名来解决这个问题

void doSth() override
Run Code Online (Sandbox Code Playgroud)

到:

__attribute__((used)) void doSth() override
Run Code Online (Sandbox Code Playgroud)

但这是——正如我上面所说的——而是一种解决方法,而不是真正的解决方案。有谁知道为什么 Clang 无法处理这个问题?

下面是编译器资源管理器和 C++ 见解的链接以及最小的可重现示例,这些示例是从我的代码库中提取的,并减少到绝对最小值,以便重现该示例并显示我的意图。

编译器浏览器:https://godbolt.org/z/83dMMvozn

C++ 见解:https://cppinsights.io/s/e35151b2

更新

我删除了不需要的代码部分,并添加了另一个(可接受的)解决方法 - 我没有使用 lambda 作为“调用”调用的参数,而是使用了可调用结构 - 它“神奇地”起作用了 - 但是,我不明白为什么它不适用于 lambda。

#include <memory>
#include <type_traits>
#include <variant>
#include <vector>
#include <list>

struct …
Run Code Online (Sandbox Code Playgroud)

c++ clang template-meta-programming c++20 c++-templates

9
推荐指数
0
解决办法
364
查看次数

为什么模板不丢弃 co_return?

我想创建一个具有同步和协程版本的函数,而不使用模板专门化,即使用if constexpr.

这是我写的函数:

template <Async _a>
AsyncResult<int, _a> func(int a) {
  if constexpr (_a == Async::Disable)
    return a;
  else
    co_return a;
}
Run Code Online (Sandbox Code Playgroud)

但是当我实例化真正的分支时,它给出了一个错误

auto a = func<Async::Disable>(1); // compiler error
auto b = func<Async::Enable>(2);  // ok
Run Code Online (Sandbox Code Playgroud)
error: unable to find the promise type for this coroutine
Run Code Online (Sandbox Code Playgroud)

为什么这不起作用?

包含 Promise 类型实现的完整代码

c++ c++20 c++-coroutine c++-templates

8
推荐指数
1
解决办法
280
查看次数

启用非模板成员函数当且仅当它会进行类型检查

例如

template <class T1, class T2>
class foo
{
    T1 t1;
    T2 t2;

    T1 bar(); //Always exists
    decltype(t1(t2)) baz(); //Should only exist if t1(t2) is valid
};
Run Code Online (Sandbox Code Playgroud)

如果baz无效,我仍然希望程序能够编译,只要没有人真正调用baz.

c++ sfinae c++11 c++-templates

4
推荐指数
1
解决办法
127
查看次数

具有部分固定参数列表的 C++ 模板

考虑如下情况:

template<typename T>
class A { ... };

template<typename T, typename DataType = std::vector<A<T>>>
class B {
  ....
  DataType data;
  ...
}
Run Code Online (Sandbox Code Playgroud)

在我的例子中DataType,类型可以是任何std“容器”,但它必须始终专门针对类型A。A的使用对于类B之外应该是透明的,但是在没有默认类型的B的定义中DataType应该明确指定例如B<int, std::deque<A<int>>。我想消除这种可能性并实现类似的目标:

template<typename T, typename container = std::vector>
class B{
  using DataType = container<A<T>>;
  ...
}
Run Code Online (Sandbox Code Playgroud)

这样我就可以专注于 B 之类的B<int, std::vector>。当然它不能完全像这样,因为container在这种情况下应该是一个完整的类型,然后必须专门化。有没有办法用 c++14 来实现这一点?

c++ templates c++-templates

4
推荐指数
1
解决办法
319
查看次数

重载函数作为模板函数的参数

我偶然发现了模板的问题,clang++我不知道如何解决。

我希望使用以类和函数作为参数的模板化函数。但是当函数是重载函数时,我得到的错误candidate template ignored: couldn't infer template argument 'F'是完全合理的。

例如,示例程序

#include <iostream>
#include <math.h>

template<class T, typename F>
T foo(F f,T x){
  return f(x);
}

int main(void) {
  std::cout << "foo(sinf,0.34) is " << foo(sinf,0.34) << std::endl;
  return 0;
}
Run Code Online (Sandbox Code Playgroud)

完全按照我想要的方式工作。现在,如果我使用重载函数(例如,sin而不是 )sinf,则无法推断模板参数。如何更改 的函数模板foo以帮助编译器解析 的类型F

c++ clang clang++ c++-templates

4
推荐指数
2
解决办法
83
查看次数

如何使用概念将参数传递给类方法?

我有一个Optional_monadic 类,它是从 std::Optional 类继承的

    template <class T>
    class monadic_optional : public std::optional<T>
    {
    public:
      using std::optional<T>::optional;
      monadic_optional(T value) : std::optional<T>(value) {}
    }
Run Code Online (Sandbox Code Playgroud)

在这堂课中我描述了该方法

    template <class Return>
    nonstd::monadic_optional<Return> and_then(std::function<nonstd::monadic_optional<Return>(T)> func)
    {
     if (this->has_value())
        return func(this->value());
     else
        return std::nullopt;
    }
Run Code Online (Sandbox Code Playgroud)

我想使用概念将模板传递给检查它是否是函数的方法。我如何使用概念来实现这一点?

    template <class T>
    concept convertible_to_func = std::convertible_to <T, std::function<nonstd::monadic_optional<Return>(T)>>
    requires
    {
    };
    nonstd::monadic_optional<T> and_then(T func)
    {
     if (this->has_value())
        return func(this->value());
     else
        return std::nullopt;
    }
Run Code Online (Sandbox Code Playgroud)

它应该看起来像这样,但它无法编译。

c++ c++-concepts c++20 c++-templates

3
推荐指数
1
解决办法
153
查看次数

理解概念。检查成员是否是静态的

假设我们有一个简单的概念,例如:

template <typename T>
concept MyConcept = requires {
    T::value == 42; 
};
Run Code Online (Sandbox Code Playgroud)

根据我的理解,这个概念是说,如果代码T::value == 42对于类型 T 有效,我就会通过。所以value必须是静态成员,对吗?

我有一个struct

struct Test { int value = 0; }
Run Code Online (Sandbox Code Playgroud)

和下一个模板函数

template <MyConcept T>
void call() { /*...*/ }
Run Code Online (Sandbox Code Playgroud)

当我尝试这样做时:

int main()
{
    call<Test>();
}
Run Code Online (Sandbox Code Playgroud)

有用!

问题是:它为什么有效? Test::value == 42不是该类型的有效代码Test

我找到了一种修复它的方法,例如:

template <typename T>
concept MyConcept = requires {
    *(&T::value) == 42; 
};
Run Code Online (Sandbox Code Playgroud)

它按预期“工作”:

<source>:6:20: note: the required expression '((* & T::value) == 42)' is …
Run Code Online (Sandbox Code Playgroud)

c++ templates c++-concepts c++20 c++-templates

3
推荐指数
1
解决办法
342
查看次数

如何检查类型是否源自向量/列表/双端队列?

我正在使用 C++20。我能够检查类型是否是某种向量/双端队列/列表,如下所示:

template <typename N> struct is_listish_trait {
  static constexpr bool value = 0;
};

/* These are listish specializations */
template <typename N, typename A> struct is_listish_trait<std::vector<N, A>> {
  static constexpr bool value = 1;
};

template <typename N, typename A> struct is_listish_trait<std::list<N, A>> {
  static constexpr bool value = 1;
};

template <typename N, typename A> struct is_listish_trait<std::deque<N, A>> {
  static constexpr bool value = 1;
};

template <typename T>
static constexpr bool is_listish = is_listish_trait<T>::value; …
Run Code Online (Sandbox Code Playgroud)

c++ templates type-traits c++20 c++-templates

3
推荐指数
1
解决办法
176
查看次数

在模板函数尾随返回类型中使用 type_identity 的目的

使用 type_identity 作为尾随返回类型的目的是什么?

https://en.cppreference.com/w/cpp/types/add_reference提供了以下可能的 add_lvalue_reference 和 add_rvalue_reference 实现

namespace detail {
 
template <class T>
struct type_identity { using type = T; }; // or use std::type_identity (since C++20)
 
template <class T> // Note that `cv void&` is a substitution failure
auto try_add_lvalue_reference(int) -> type_identity<T&>;
template <class T> // Handle T = cv void case
auto try_add_lvalue_reference(...) -> type_identity<T>;
 
template <class T>
auto try_add_rvalue_reference(int) -> type_identity<T&&>;
template <class T>
auto try_add_rvalue_reference(...) -> type_identity<T>;
 
} // namespace detail
 
template <class T> …
Run Code Online (Sandbox Code Playgroud)

c++ std type-traits c++-templates

2
推荐指数
1
解决办法
126
查看次数

使用 SFINAE 禁用类模板的非模板化成员函数的最佳方法?

有很多类似的问题,但我找不到这个问题:给定

template <typename T>
struct S {
    int f(int x) const { return x; }
};
Run Code Online (Sandbox Code Playgroud)

我希望能够f根据T. 显而易见的事情不起作用:

#include <type_traits>

template <typename T>
struct MyProperty { static constexpr auto value = std::is_same_v<T, float>; };

template <typename T>
struct S {
    template <typename = std::enable_if_t<MyProperty<T>::value>>
    int f(int x) const { return x; }
};

int main() {
    S<int> s;
}
Run Code Online (Sandbox Code Playgroud)

我能想到的最接近的是一个假人typename U = T

    template <typename U = T, typename = std::enable_if_t<MyProperty<U>::value>>
    int f(int x) …
Run Code Online (Sandbox Code Playgroud)

c++ c++-templates

2
推荐指数
1
解决办法
149
查看次数

如何使 `std::conditional_t` 与递归一起工作?

我的用例如下:

template <typename T>
struct peel {
    using type = std::conditional_t<std::is_pointer_v<T>, typename peel<std::remove_pointer_t<T>>::type, T>;
};

template <typename T>
using peel_t = typename peel<T>::type;
Run Code Online (Sandbox Code Playgroud)

但是,如果您调用此方法,peel_t<int *>则会返回错误:

错误:“peel<int>”中没有名为“type”的类型

问题似乎是缺乏终止条件,因为peel没有专门处理没有更多指针需要删除的情况。但我脑子里std::conditional_t应该已经在处理这个问题了。

我的问题是,有没有一种方法可以std::conditional_t在需要递归的场景中工作(比如这个)?我特别希望通过以下检查:

static_assert(std::is_same_v<peel_t<int **>, int>);
Run Code Online (Sandbox Code Playgroud)

c++ type-traits template-meta-programming c++-templates

2
推荐指数
1
解决办法
116
查看次数

如何仅将选定的参数传递给函数?

假设我有一个结构体 X,我想根据某些条件填写所有字段。我想区分要传递给函数的参数,并根据提供的参数创建然后返回该对象。我很好奇是否应该在 C++ 中使用可变参数模板?伪代码。

#include <iostream>

using namespace std;

struct X
{
    string a;
    string b;
    string c;
}

X generateTransaction(const string& e, const string& f, const string& g)
{
    X one;
    if (e)
        one.a = e;
    if (f)
        one.b = f;
    if (g)
        one.c = g;
    return one;
}

int main()
{
    generateTransaction(e = "first");
    generateTransaction(e = "abc", f = "second");
    generateTransaction(g = "third");
    generateTransaction(e = "test", g = "third");
    generateTransaction("m", "n", "o");
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

c++ variadic-templates c++17 c++-templates

1
推荐指数
2
解决办法
212
查看次数