C++11/14是否支持类似于vector<auto>的东西?

5

我想知道c++11/c++14是否支持类似vector<auto>的东西?如果不支持,有什么原因吗?


2
它将如何被使用? - Cameron
1
也许可以在C++999中这样做,这样你就可以向向量中“push_back”任何东西了。 :P - herohuyongtao
@卡梅伦,有了它我们可以写更通用的代码。 - da man
3
@daman:我的意思是,你能提供一个例子吗?我不清楚它的语义会是什么。 - Cameron
@卡梅伦,这是一个例子:http://stackoverflow.com/questions/26111448/how-to-handle-class-with-multiple-data-members-of-different-type-in-c - da man
1
std::vector< boost::any >std::vector< boost::variant<type0, type1, type2, type_and_so_on> > - dyp
3个回答

8

这并不是直接支持的功能,也不清楚您想要做什么。

评论中已经提到了一些可能性(例如Boost的anyvariant类)用于创建异构集合。希望这不是您想要的,因为异构集合与C++不太匹配,使用它们很丑陋和笨拙。我想在某些情况下/情况下,确实会是可用的最佳选择,但至少在我的经验中,这些情况相当罕见。

另一个可能的解释是你想要一个向量(与一般的auto相似),它恰好持有一种类型,但该类型是从初始化程序中推导出来的。因此,如果你用一些int初始化向量,你将得到一个vector,如果你用一些字符串初始化它,你将得到一个vector等等。虽然语言不直接支持这个功能,但在某种程度上模拟它相当容易。模板类不能/不会推导模板参数,但模板函数可以。因此,我们可以创建一个小的函数模板来获取一些初始化程序,推导它们的类型,并返回该类型的向量。例如:
template <class T>
std::vector<T> make_vector(std::initializer_list<T> init) {
    return std::vector<T>(init);
}

这将返回一个vector<T>(其中T是从初始化列表中数据类型推导出的),因此您可以执行以下操作:
auto a = make_vector({ 1, 2, 3, 4 });       // a -> vector<int>
auto b = make_vector({ 1.0, 2.0, 3.0 });    // b -> vector<double>
auto c = make_vector({ "1"s, "2"s, "3"s }); // c -> vector<std::string>

那个最后一个需要一个用户定义的字面量运算符,这是C++14中新的(许多编译器尚不支持)。其余部分应该可以使用C++11。此外,也有一些讨论(以及N3602中的提议)关于添加能力(可能是在C++17中),使您能够像上面的make_vector一样定义模板构造函数。这将允许您在构造函数上使用参数推断来推断整个类的模板参数,因此您可以执行类似以下内容的操作:
X x(1);     // deduces as X<int>
X x(2.0)    // deduces as X<double>

注意:尽管已经有人提出了这个想法,但它并未被接受。它可能(很容易)永远不会被接受 - 即使被接受了,也可能在那之前发生重大变化。


我相信你正在考虑的提案是N3602,但我认为它目前没有任何进展。 - Jonathan Wakely
@dyp:经过一些思考(和一点阅读),我认为你是对的 - 至少在这种情况下(也许是普遍情况,但我不太确定),按值传递无疑是最合理的选择(我已经相应地进行了编辑)。 - Jerry Coffin
@JonathanWakely:感谢提供链接 - 是的,那就是我想到的那个(我已经将链接编辑到答案中,并加入了更强烈的警告,即它可能永远不会发生)。 - Jerry Coffin

4

不,C++11和C++14已经完成并发布了,没有这个功能。

但是,在Concepts工作的框架下,可能会出现vector<auto>和类似的东西,比如tuple<auto...>

这很自然,因为std::vector<T>可以在函数模板和部分特化的类模板中使用,其中T是一个模板参数,而且多态lambda允许auto作为函数参数类型(这是带有推断参数的函数模板的简写)。

概念技术规范允许像这样声明“泛型函数”:

auto func(auto arg);

由于您可以拥有这样的函数模板:

template<typename T>
  auto func(std::vector<T> v);

将通用函数语法扩展以允许:

auto func(std::vector<auto> v);

一旦您在函数声明中允许这样做,那么也应该可以在变量声明中允许这样做:

std::vector<auto> v = function_returning_vector_of_something();

它没有出现在C++11中的原因是,auto是新功能,尝试让它做太多事情会过于雄心勃勃。在C++14中,多态lambda表达式是新功能,再次扩展auto的用途将是雄心勃勃的。

对于C++17,我们在实际代码中使用auto的经验更丰富,编译器编写者熟悉实现它并知道不需要太多努力就可以做到什么。


дҪ жҳҜиҜҙеңЁC++17дёӯзј–иҜ‘еҷЁе°ҶиғҪеӨҹд»Һinitializer_listжҺЁж–ӯеҮәstd::vector<T>зҡ„Tеҗ—пјҹзӣ®еүҚfunc({1, 2, 3, 4})ж— жі•е·ҘдҪңгҖӮ - Jamboree
我并不是说它一定能做什么,现在还为时过早。那个例子的重点不在于初始化列表,而在于使用auto,所以我已经改变了那个例子。 - Jonathan Wakely

1

boost::any 可以存储任何可复制的类型的实例,这是非常多的类型。

为了从你的 any 中获取数据,你必须知道你存储在其中的确切类型。

编写一个简单的 any 并不难:

#include <memory>
#include <utility>

struct any_internal {
  virtual any_internal* clone() const = 0;
  virtual ~any_internal() {};
};
template<class T>
struct any_details;
class any {
  std::unique_ptr<any_internal> internal;
public:
  any() = default;

  any( any && ) = default;
  any( any const&& o):any(o) {}
  any( any & o ):any( const_cast<any const&>(o) ) {}

  any& operator=( any && ) = default;
  any& operator=( any const&& o ) { return this->operator=( o ); };
  any& operator=( any & o ) { return this->operator=( const_cast<any const&>(o) ); };

  any( any const& o ):internal( o.internal?o.internal->clone():nullptr ) {}
  any& operator=( any const& o ) {
    any tmp(o);
    using std::swap;
    swap( internal, tmp.internal );
    return *this;
  }

  template<class U>
  void reset( U&& o );
  template<class U, class... Args>
  void emplace( Args&&... args );
  template<class U>
  any( U&& o );
  template<class U>
  any& operator=(U&& o);
  template<class T> T* get();
  template<class T> T const* get() const;
  template<class T> T* fast_get();
  template<class T> T const* fast_get() const;
  explicit operator bool() const { return internal!=nullptr; }
};
template<class T>
struct any_details : any_internal {
  T t;
  template<class...Args>
  any_details( Args&&... args ):t(std::forward<Args>(args)...) {}
  any_internal* clone() const override { return new any_details<T>{t}; }
};
template<class U, class... Args>
void any::emplace( Args&&... args ) {
  internal.reset( new any_details<U>( std::forward<Args>(args)... ) );
}
template<class U>
void any::reset( U&& o ) {
  emplace<typename std::decay<U>::type>( std::forward<U>(o) );
}
template<class U>
any::any( U&& o ) {
  reset( std::forward<U>(o) );
}
template<class U>
any& any::operator=(U&& o) {
  reset( std::forward<U>(o) );
  return *this;
}
template<class T> T* any::get() {
  auto* r = dynamic_cast< any_details<T>* >( internal.get() );
  if (r) return &r->t;
  return nullptr;
}
template<class T> T const* any::get() const {
  auto* r = dynamic_cast< any_details<T>* >( internal.get() );
  if (r) return &r->t;
  return nullptr;
}
template<class T> T* any::fast_get() {
  auto* r = static_cast< any_details<T>* >( internal.get() );
  if (r) return &r->t;
  return nullptr;
}
template<class T> T const* any::fast_get() const {
  auto* r = static_cast< any_details<T>* >( internal.get() );
  if (r) return &r->t;
  return nullptr;
}

一个 std::vector<any> 的行为很像你希望 std::vector<auto> 做的。

通过小缓冲区优化(即,如果 t 很小,则在 any 中存储 T,而不是使用堆),可以实现增加效率。

你可能还想将 getfast_get 分开,其中 get 执行 dynamic_cast,而 fast_get 执行 static_cast,以提高效率。(当你确定时,可以使用 fast_get

基本上这是一个装饰过的 void*


为什么需要 any const&&any& 的拷贝构造函数? - dyp
swap( internal, tmp.internal ); 或者 internal = std::move(tmp.internal); - dyp
1
@dyp 我有一个通用的复制构造函数和赋值操作符,我明确表示不在rhs是“any”时调用它。是的,move也可以工作:我正在做复制交换,然后懒得实现void swap(any&, any&),因为我注意到它只有两行长。感谢修复--这是即兴写的。 - Yakk - Adam Nevraumont
@dyp 哦天啊,那绝对需要修复。 - Yakk - Adam Nevraumont
1
@dyp 12.8/18 "如果类X的条件成立,则隐式声明的复制赋值运算符将采用X& X::operator=(const X&)的形式,否则,隐式声明的复制赋值运算符将采用X& X::operator=(X&)的形式" -- 8.4.2/1 "显式默认的函数应该:...具有与隐式声明相同的声明函数类型(...)..." -- foo& operator(foo)不是任何隐式声明特殊成员函数的声明函数类型,因此不能=default。((...)条款讨论了两个例外情况,但都不适用) - Yakk - Adam Nevraumont
显示剩余4条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接