我的班级的可变参数模板构造函数无法修改我的班级成员,为什么呢?

时间:2019-04-13 13:06:30

标签: c++ c++11 recursion variadic-templates

我一直在努力完成我们教授所完成的任务,我必须围绕可变参数模板类进行工作。 问题是,我无法在递归构造函数中修改类成员。我不知道为什么会这样,一旦进入下一个构造函数调用,它将丢弃我对变量的更改。

我尝试过的事情:

  • 使用指针int* count代替int count

  • 使用设置器来设置counter

我已经在Google周围搜索了几个小时,但是找不到解决方法的答案。

头文件“ test.h”:

#include <cstdarg>
#include <iostream>

class Counter {
private:
    int count = 0;
    int tmp;

public:
    template <typename... Rest> Counter (int t, Rest... rest) {
        count++;
        std::cout << "start recursive number " << count << "...\n";
        Counter(rest ...);
        tmp = t;
        std::cout << "end recursive number " << count << "...\n";
    }
    Counter (int t) {
        count++;
        tmp = t;
        std::cout << "reached end of recursive ->  " << count << "\n";
    }
};

main.cpp:

#include "test.h"
int main () {
    Counter a {0, 1, 2, 3, 4};
}

我得到的输出:

start recursive number 1...
start recursive number 1...
start recursive number 1...
start recursive number 1...
reached end of recursive ->  1
end recursive number 1...
end recursive number 1...
end recursive number 1...
end recursive number 1...

2 个答案:

答案 0 :(得分:24)

Counter(rest ...);创建一个未命名的临时对象,它不会递归调用此对象的构造函数。每个对象都有自己的count生成,因此您得到的流为1 1 1 1

如果要将对象初始化委托给其他构造函数,则它应该出现在成员初始化列表中。不过,这似乎不是一个好主意:

template <typename... Rest> Counter (int t, Rest... rest)
:   Counter{rest...}
{
    count++;
    std::cout << "start recursive number " << count << "...\n";
    tmp = t;
    std::cout << "end recursive number " << count << "...\n";
}

答案 1 :(得分:12)

如VTT所述,在构造函数的主体内部调用Counter()将创建一个新的Counter()对象。

您可以递归地调用构造函数,但是您必须在初始化列表中进行此操作:查找“代理构造函数”以获取更多信息。

我也建议您不要在构造函数体内初始化(和修改)成员对象。

如果您的目标使用参数的数量初始化count并使用最后一个参数的值初始化tmp,则我提出以下(基于“标签分配”的)解决方案

class Counter
 {
   private:
      struct tag
       { };

      int count = 0;
      int tmp;

      Counter (tag tg, std::size_t c0, int t) : count(c0), tmp{t}
       { std::cout << "end: " << tmp << ", " <<count << "\n"; }

      template <typename... Rest>
      Counter (tag t0, std::size_t c0, int t, Rest... rest) 
         : Counter{t0, c0, rest...} 
       { std::cout << "recursion: " << tmp << ", " << count << "\n"; }

   public:
      template <typename... Rest>
      Counter (Rest... rest) : Counter{tag{}, sizeof...(Rest), rest...} 
       { std::cout << "start: " << tmp << ", " << count << "\n"; }
 };

您还可以避免标签分发和构造函数递归将关于rest...的递归委托给用于初始化{{1的方法(可能是static,还有constexpr) }}

tmp

主题外:确切地说,您的class Counter { private: int count = 0; int tmp; static int getLastInt (int i) { return i; } template <typename ... Rest> static int getLastInt (int, Rest ... rs) { return getLastInt(rs...); } public: template <typename... Rest> Counter (Rest... rest) : count(sizeof...(Rest)), tmp{getLastInt(rest...)} { std::cout << tmp << ", " << count << "\n"; } }; 类不是“可变参数模板类”。

这是一个普通的(不是模板)类,带有一个(两个,在我的第一个解决方案中)可变参数模板构造器。

-编辑-

OP询问

  

如果我需要将计数作为静态const变量和int数组(计数器长度在编译时间内)作为类成员获取,该怎么办? (数组将使用所有构造函数参数填充)这是否在C ++的可能性之内?

仅当计数器是该类的所有实例之间的公共值时,静态const(也许也Counter)才有意义。

此刻没有意义,因为您的constexpr接受不同长度的初始化列表。

但是假设构造函数的参数编号是模板参数(例如Counter)...在这种情况下,N就是count,可以是{{1} }。您可以为值定义N(也可以是static constexpr,但我建议尽量避免使用C样式的数组,而应使用std::array<int, N>),并使构造函数{ {1}},您可以强制进行编译时初始化。

以下是完整的C ++ 14示例(不幸的是,使用int[N]std::array仅从C ++ 14开始可用)。

观察到我已经将constexpr中的std::make_index_sequence变量定义为std::index_sequence:只有这样,您才能(假设没有原样)强加{{ 1}}已初始化编译时间

f8

如果可以使用启用了C ++ 17的编译器,还可以使用main()的推导指南

constexpr

因此无需重复定义f8的模板参数

#include <array>
#include <iostream>
#include <type_traits>

template <typename T, std::size_t>
using getType = T;

template <std::size_t N, typename = std::make_index_sequence<N>>
struct foo;

template <std::size_t N, std::size_t ... Is>
struct foo<N, std::index_sequence<Is...>>
 {
   static_assert( sizeof...(Is), "!" );

   static constexpr auto count = N;

   const std::array<int, N> arr;

   constexpr foo (getType<int, Is> ... is) : arr {{ is ... }}
    { }
 };

int main ()
 {
   constexpr foo<8u>  f8 { 2, 3, 5, 7, 11, 13, 17, 19 };

   for ( auto const & i : f8.arr )
      std::cout << i << ' ';

   std::cout << std::endl;
 }

因为它是根据构造函数的参数编号推导出来的。