【问题标题】:Template default init, if there no default constructor in a class模板默认初始化,如果类中没有默认构造函数
【发布时间】:2020-09-28 14:15:08
【问题描述】:

我是 C++ 的新手。我写了一个函数模板来总结向量中的所有元素,如下所示:

template<typename T>
inline T sumator(const std::vector<T> &sequence) {
    T init = T{};
    return accumulate(sequence.begin(), sequence.end(), init);
}

在不声明默认构造函数之前一切正常,而是从例如 2 个参数声明构造函数。 像这样:

class A {
    private:
        int a;
        int b;
    public:
//        A() = default;
        A(int aa, int bb) : a(aa),b(bb){};
        A operator+(const A &right) const {
            return {a + right.a, b + right.b};
        }
        bool operator==(const A &right) const {
            return (a == right.a && b == right.b);
        }

    };
    vector<A> bc = {{3,5},{5,5}};
    A res = Adder::sumator(works);
    A result = {8,10};
    assert(result,res);

现在我收到如下错误:错误:没有匹配的构造函数用于初始化“A” T 初始化 = T{};

如何避免这种情况,不使用https://en.cppreference.com/w/cpp/types/is_default_constructible

UPD:我有很多类,其中一些具有默认构造函数,一些没有但具有用于 2 arg 的构造函数,一些用于 3,等等。而我要好好积累每一种情况

【问题讨论】:

标签: c++ c++11 c++17


【解决方案1】:

我能想到的可能解决方案。

  1. 通过提供初始值让用户依赖std::accumulate。
  2. 为sumator 提供一个带有初始值的重载。如果用户的类型没有默认构造函数,用户可以使用初始值调用重载。
  3. 坚持调用函数的类型有一个默认构造函数。
template<typename T>
inline T sumator(const std::vector<T> &sequence, T init) {
    return accumulate(sequence.begin(), sequence.end(), init);
}

template<typename T>
inline T sumator(const std::vector<T> &sequence) {
    return accumulate(sequence.begin(), sequence.end(), {});
}

【讨论】:

  • 谢谢,但请您解释一下第一个答案。也许我应该使用特征,否则对我没有帮助?
  • @ВладимирКавиа 用户可以通过提供初始值直接使用std::accumulate,不使用sumator。
  • @ВладимирКавиа,这是个好问题。如果您无法在 SO 中找到答案,您可以在新帖子中提问。
【解决方案2】:

您可以使用默认参数添加另一个参数。

template<typename T>
inline T sumator(const std::vector<T> &sequence, const T& init = T{}) {
    return accumulate(sequence.begin(), sequence.end(), init);
}

对于可以默认构造的类型,您仍然可以

sumator(some_vector);

并在T不能默认构造时指定默认值。例如

sumator(some_vector, A{0, 0});

【讨论】:

    【解决方案3】:

    如果有人试图以错误的方式使用它,您可以将其拆分为两个重载并添加 static_assert 以提供很好的编译错误消息。

    只有当类型是默认可构造的时,一个重载才有效,并且一个重载对于那些不是的类型有效:

    #include <type_traits>
    #include <vector>
    
    template<typename T>
    T sumator(const std::vector<T>& sequence) {
        static_assert(std::is_default_constructible_v<T>);
        return std::accumulate(sequence.begin(), sequence.end(), T{});
    }
    
    template<typename T>
    T sumator(const std::vector<T>& sequence, const T& init) {
        return std::accumulate(sequence.begin(), sequence.end(), init);
    }
    

    另一种选择是添加一个默认参数,该参数可用于默认可构造的类型 - 您还可以使其更通用,使其不仅适用于vectors,还适用于lists 等。

    template<template<class, class...> class C, class T, class... Ts>
    T sumator(const C<T, Ts...>& sequence, const T& init = T{}) {
        return std::accumulate(sequence.begin(), sequence.end(), init);
    }
    

    【讨论】:

      猜你喜欢
      • 2014-05-18
      • 2021-12-16
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-06-12
      相关资源
      最近更新 更多