C++:如何从 make_shared 部分推导模板参数

C++: How to partially deduce template arguments from make_shared

为了规避 ,我将要从中推导出 class 模板参数 (Internal) 的结构嵌​​入到第二个结构 (Container).

我想让代码的用户能够创建例如结果类型的共享指针。通过在结构中编写我自己的 create 函数,这工作得很好。

#include <memory>

/// Container that is used in order to partially specify template arguments
template <int A> struct Container {

  /// Contained type, of which the template arguments are deduced.
  template <int B> struct Internal {
    explicit Internal(std::integral_constant<int, B> fu) { (void)fu; }
  };

  /// Helper function
  template <int C>
  [[nodiscard]] static auto create(std::integral_constant<int, C> t) noexcept {
    return std::make_shared<Container<A>::Internal<C>>(t);
  }
};


int main() {
  Container<1>::Internal works{std::integral_constant<int, 8>{}};
  auto const worksAswell = Container<1>::create(std::integral_constant<int, 8>{});
}

但是当我尝试直接使用 make_shared 时,它失败了。我想让用户使用例如std::make_shared 函数。

int main() {
  auto const fails = std::make_shared<Container<1>::Internal>(std::integral_constant<int, 8>{}); 
}

据我所知,这失败了,因为我不能部分指定模板参数,如果我不想指定所有模板参数,我将无法从 make_shared 函数中推断出它们。


main.cc: In function ‘int main()’:
main.cc:21:74: error: no matching function for call to ‘make_shared<1>(std::integral_constant<int, 8>)’
   21 |   auto const fails = std::make_shared<1>(std::integral_constant<int, 8>{});
      |                                                                          ^
In file included from /usr/include/c++/9.2.0/memory:81,
                 from /home/juli/main9.cc:1:
/usr/include/c++/9.2.0/bits/shared_ptr.h:714:5: note: candidate: ‘template<class _Tp, class ... _Args> std::shared_ptr<_Tp> std::make_shared(_Args&& ...)’
  714 |     make_shared(_Args&&... __args)
      |     ^~~~~~~~~~~
/usr/include/c++/9.2.0/bits/shared_ptr.h:714:5: note:   template argument deduction/substitution failed:

是否可以启用像 std::make_shared 这样的生成器函数来部分推导出这样的模板参数?完整代码可以在here.

找到

如果您创建自己的接受模板模板参数的 make_shared,我们可以使用 decltype 推断结果类型并将其传递给 std::make_shared

#include <memory>
#include <type_traits>

/// Container that is used in order to partially specify template arguments
template <int A> struct Container {

  /// Contained type, of which the template arguments are deduced.
  template <int B> struct Internal {
    explicit Internal(std::integral_constant<int, B> fu) { (void)fu; }
  };
};

template <template <int> typename partial, typename... Args>
auto make_shared(Args&&... args) {
    using target_type = std::remove_pointer_t<decltype(new partial{std::declval<Args>()...})>;
    return std::make_shared<target_type>(std::forward<Args>(args)...);
}

using std::make_shared;

int main() {
  auto const fails = make_shared<Container<1>::Internal>(std::integral_constant<int, 8>{});
  static_assert(std::is_same_v<const std::shared_ptr<Container<1>::Internal<8>>, decltype(fails)>);
}

这里唯一的问题是我们的 make_shared 需要知道预期目标的模板签名。

积极的一面是,我们可以为不同的模板签名添加多个重载,并且我们可以使用一个参数包。