шаблон переменной и std :: function - PullRequest
0 голосов
/ 27 августа 2018

В следующем коде:

#include <functional>
#include <iostream>
#include <tuple>

template <typename... t>
class a {
 public:
  explicit a(std::function<std::tuple<t...>()>&& p_d,
             std::function<bool(t...)>&& p_f)
      : m_d(std::move(p_d)), m_f(std::move(p_f)) {}

  bool operator()() { return m_f(m_d()); }

 private:
  std::function<std::tuple<t...>()> m_d;
  std::function<bool(t...)> m_f;
};

class d {
  std::tuple<int, float, std::string&&> operator()() {
    return std::tuple<int, float, std::string&&>(-9, 3.14, "olá!");
  }
};

class f {
  bool operator()(int p_i, float p_f, std::string&& p_s) {
    std::cout << "i = " << p_i << ", f = " << p_f << ", s = " << p_s
              << std::endl;
    return true;
  }
};

int main() {
  d _d;
  f _f;
  typedef a<int, float, std::string&&> a_t;

  a_t _a(std::move(_d), std::move(_f));

  _a();

  return 0;
}

Я получаю ошибку компилятора:

../untitled014/main.cpp: In function ‘int main()’:
../untitled014/main.cpp:38:38: error: no matching function for call to ‘a<int, float, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >&&>::a(std::remove_reference<d&>::type, std::remove_reference<f&>::type)’
   a_t _a(std::move(_d), std::move(_f));
                                      ^
../untitled014/main.cpp:8:12: note: candidate: a<t>::a(std::function<std::tuple<_Elements ...>()>&&, std::function<bool(t ...)>&&) [with t = {int, float, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >&&}]
   explicit a(std::function<std::tuple<t...>()>&& p_d,
            ^
../untitled014/main.cpp:8:12: note:   no known conversion for argument 1 from ‘std::remove_reference<d&>::type {aka d}’ to ‘std::function<std::tuple<int, float, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >&&>()>&&’

Я не понимаю, почему компилятор не может найти подходящее преобразование, как об этом сообщается в последней строке ошибки.

Я использую g ++ с флагом '-std = c ++ 14' на коробке Xubuntu, а 'g ++ --version' сообщает 'g ++ (Ubuntu 5.4.0-6ubuntu1 ~ 16.04.10) 5.4.0 20160609'

Может кто-нибудь сказать мне, что я делаю не так?

Спасибо !!

1 Ответ

0 голосов
/ 27 августа 2018

Я вижу как минимум три ошибки в вашем коде.

В произвольном порядке ...

(1) если вы хотите функционал, operator() должен быть public; вы делаете они оба private

class d { // default for a class is private, so operator() is private
  std::tuple<int, float, std::string&&> operator()() {
    return std::tuple<int, float, std::string&&>(-9, 3.14, "olá!");
  }
};

class f { // default per a class is private, so operator() is private
  bool operator()(int p_i, float p_f, std::string&& p_s) {
    std::cout << "i = " << p_i << ", f = " << p_f << ", s = " << p_s
              << std::endl;
    return true;
  }
};

Вы можете решить эту проблему, сделав operator() public или, может быть, проще, сделав d и f struct s.

(2) "olá!" не является допустимым значением для инициализации std::string &&

 std::tuple<int, float, std::string&&>(-9, 3.14, "olá!");

Вы можете скомпилировать с

 std::tuple<int, float, std::string&&>(-9, 3.14, std::string{"olá!"});

но это неправильная идея, потому что таким образом вы инициализируете объект ссылкой на объект, который уничтожается сразу после этого, и получаете висящую ссылку внутри кортежа.

Мне не ясно, почему вы хотите использовать std::string && в кортеже, но я подозреваю, что вы можете использовать std::string, а не ссылку на него. Во всем вашем коде, не только в этой функции.

В этом случае следующий код

std::tuple<int, float, std::string>(-9, 3.14, "olá!");

работает.

Если вы действительно хотите ссылку на std::string в своем кортеже, вы должны передать ссылку на объект с продолжительностью жизни, достаточной для покрытия времени жизни вашего кортежа.

(3) operator() из d возвращает std::tuple<int, float, std::string&&>, где operator() из f принимает три параметра: int, float и std::string&&.

Таким образом, вы не можете просто передать значение, возвращаемое от первого ко второму, без распаковки std::tuple некоторым способом, как вы делаете в a::operator()

bool operator()() { return m_f(m_d()); }
// ........................^^^^^^^^^^  Wrong!

Вы используете C ++ 14, поэтому вы не можете использовать std::apply() (доступно начиная с C ++ 17)

bool operator()() { return std::apply(m_f, m_d()); }
// ........................^^^^^^^^^^^^^^^^^^^^^^  Starting from C++17

так что эмулировать надо каким-то образом (std::make_index_sequence, std::index_sequence, std::get() и т. Д.).

К примеру

  bool operator() ()
   { return call(std::make_index_sequence<sizeof...(t)>{}); }

  template <std::size_t ... Is>
  auto call (std::index_sequence<Is...> const &)
   {
     auto tmp { m_d() }; // so m_d() is called only one time

     return m_f(static_cast<t>(std::get<Is>(tmp))...);
   }

, где call() может быть private

...