【问题标题】:variadic template and std::function可变参数模板和 std::function
【发布时间】:2018-08-27 11:51:32
【问题描述】:

在以下代码中:

#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> >&&>()>&&’

我不明白为什么编译器找不到合适的转换,因为它在错误的最后一行报告。

我在 Xubuntu 机器上使用带有标志“-std=c++14”的 g++,并且“g++ --version”报告“g++ (Ubuntu 5.4.0-6ubuntu1~16.04.10) 5.4.0 20160609”

谁能告诉我我做错了什么?

谢谢!!

【问题讨论】:

  • 不管其他问题如何,您的d::operator() 提供了对string 的悬空引用。

标签: c++ templates c++14 variadic-templates std-function


【解决方案1】:

我在您的代码中看到至少三个错误。

排名不分先后……

(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 structs。

(2) "olá!" 不是初始化std::string &amp;&amp; 的有效值

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

你可以编译

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

但这是错误的想法,因为这样一来,您使用对在之后立即销毁的对象的引用来初始化对象,并且您在元组中获得了一个悬空引用。

我不清楚为什么要在元组中使用std::string &amp;&amp;,但我怀疑你可以使用std::string,而不是对它的引用。在你的所有代码中,不只是在这个函数中。

在这种情况下,下面的代码

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

有效。

如果你真的想在你的元组中引用一个std::string,你必须传递一个对生命周期足够长的对象的引用,以覆盖你的元组的生命周期。

(3) d 的operator() 返回一个std::tuple&lt;int, float, std::string&amp;&amp;&gt;,其中f 的operator() 接受三个参数:int、float 和std::string&amp;&amp;。

因此,您不能像在 a::operator() 中那样,在不解包 std::tuple 的情况下简单地将第一个返回的值传递给第二个。

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

【讨论】:

  • 关于 (1),我只是错过了 'f::opertator()()' 和 'd::operator()()' 是私有的......(也是)初学者的错误! 8)。我不知道(2),谢谢你的信息。关于 (3),主要问题,为了保持与 C++ 11 的兼容性,我决定改为 'bool f::operator()(std::tuple&&)'。跨度>
  • 呃,(2) 中的内容是什么?您的立即泄漏?就像,永远,永远写* new X。
  • @max66 (2) 是内存泄漏!这里没有动态分配的理由。 std::tuple&lt;int, float, std::string&amp;&amp;&gt;(-9, 3.14f,std::string{"olá!"}); 完全有效,因为 std::string{"olá!"} 是一个 R 值。
  • @Quimby ... 这是一个悬空引用。
  • @Barry 哦,你是对的。但在那种情况下,std::string&amp;&amp;&gt;(-9, 3.14f,"olá!"); 也是悬空引用,对吧?因为在元组的 ctor 中会有类似 std::string&amp;&amp; member = "olá!" 的东西,它也会创建 temp。
猜你喜欢
  • 2022-01-05
  • 2015-12-09
  • 1970-01-01
  • 1970-01-01
  • 2017-10-10
  • 2014-01-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多