Рекурсивная шаблонная функция не может назначаться переменной 'state' с типом const-квалифицированного 'const tt &' - PullRequest
2 голосов
/ 24 февраля 2020

Я сократил свой код до следующего минимального кода:

#include<iostream>
#include<vector>

class tt
{
public:
   bool player;        
   std::vector<tt> actions;
};

template<typename state_t>
int func(state_t &state, const bool is_max)
{
   state.player = true;

   const auto &actions = state.actions;
   if(state.actions.size())
   {
      auto soln = func(actions[0], false);
   }

   return 0;
}

int main(int argc, char const *argv[])
{
   tt root;
   func(root, true);

   return 0;
}

Когда я пытаюсь скомпилировать этот код, я получаю

test.cpp:14:17: error: cannot assign to variable 'state' with const-qualified type 'const tt &'
   state.player = true;
   ~~~~~~~~~~~~ ^
test.cpp:19:19: note: in instantiation of function template specialization 'func<const tt>' requested here
      auto soln = func(actions[0], false);
                  ^
test.cpp:28:4: note: in instantiation of function template specialization 'func<tt>' requested here
   func(root, true);
   ^
test.cpp:12:19: note: variable 'state' declared const here
int func(state_t &state, const bool is_max)
         ~~~~~~~~~^~~~~
1 error generated.

Он утверждает, что состояние const tt & тип. Сигнатура шаблонной функции - int func(state_t &state, const bool is_max), а перед state_t нет const. Похоже, что const каким-то образом выводится из рекурсивного вызова, потому что actions является вектором const-ref для tt объектов. Я думал, что аргумент вычитания игнорирует const? Как это может произойти?

Ответы [ 2 ]

3 голосов
/ 24 февраля 2020

Ответ в основном взят из книги Скотта Майерса Эффективная книга C ++ .

template<typename T>
void f(ParamType param);
f(expr);                // deduce T and ParamType from expr

ParamType является ссылкой или указателем, но не универсальной ссылкой

Простейшая ситуация - это когда ParamType является ссылочным типом или типом указателя, но не универсальной ссылкой. В этом случае вычитание типа работает следующим образом:

  • Если тип expr является ссылкой, игнорируйте ссылочную часть.
  • Затем сопоставьте тип выражения expr с ParamType, чтобы определить T.

В процессе вывода аргументов игнорируется ссылочная часть, а не const.

В вашем случае это const auto &actions = state.actions;, что означает для шаблона вычет аргумента auto soln = func(actions[0], false); отбрасывается только ссылочная часть, а не cv .

Дополнительные примеры из книги.

template<typename T>
void f(T& param);       // param is a reference

и у нас есть эти переменные декларации,

int x = 27;             // x is an int
const int cx = x;       // cx is a const int
const int& rx = x;      // rx is a reference to x as a const int
the deduced types for param and T in various calls are as follows:

f(x);                   // T is int, param's type is int&

f(cx);                  // T is const int,
                        // param's type is const int&

f(rx);                  // T is const int,
                        // param's type is const int&
0 голосов
/ 24 февраля 2020

В дополнение к ответу @ aep, если бы он был выведен как tt вместо const tt, компилятор также сгенерировал бы ошибку, потому что невозможно связать const reference с non-const reference без const_cast.

#include<iostream>
#include<vector>

class tt
{
public:
   bool player;        
   std::vector<tt> actions;
};

void try_to_bind_const_reference_to_non_const_reference( tt& t )
{

}

template<typename state_t>
int func(state_t &state, const bool is_max)
{
   state.player = true;

   const auto &actions = state.actions;
   if(state.actions.size())
   {
      // auto soln = func(actions[0], false);
      try_to_bind_const_reference_to_non_const_reference( actions[0] );
   }

   return 0;
}

int main(int argc, char const *argv[])
{
   tt root;
   func(root, true);

   return 0;
}

запустить онлайн

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...