1

C++ プロジェクトに取り組んでいるときに、予期しないイライラする動作に遭遇しました。私の実際のコードはもう少し複雑ですが、次の例でも同様にキャプチャされます。

class Irritating
{
    public:  Irritating() {}
    private: Irritating(const Irritating& other) {}
};

const Irritating singleton;                // Works just fine.
const Irritating array[] = {Irritating()}; // Compilation error.

int main()
{
    return 0;
}

これをコンパイルしようとすると、次のエラーが発生します (念のため GCC バージョンがスローされます)。

[holt@Michaela irritating]$ g++ --version
g++ (GCC) 4.6.3 20120306 (Red Hat 4.6.3-2)
Copyright (C) 2011 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

[holt@Michaela irritating]$ g++ test.cpp
test.cpp:4:11: error: ‘Irritating::Irritating(const Irritating&)’ is private
test.cpp:8:41: error: within this context
[holt@Michaela irritating]$ 

残念ながら、問題のオブジェクトは外部ライブラリからのものであり、私の管理外です。私の現在の回避策は、ポインターの配列を使用することです。動作しますが、少しハックな感じがし、不必要な間接レイヤーが追加されます。これを行うより良い方法はありますか?

また、配列は一定でグローバルです(実際のコードではクラス静的です)。その場で初期化されていないのはなぜですか?これは予想される C++ の動作ですか、それとも GCC のバグ/癖ですか?

更新: GCCに同意するかどうかを確認するためだけにClangをインストールしました。悲しいことに、それはしました:

[holt@Michaela irritating]$ clang test.cpp
test.cpp:8:29: warning: C++98 requires an accessible copy constructor for class 'Irritating' when binding a reference to a temporary; was private
      [-Wbind-to-temporary-copy]
const Irritating array[] = {Irritating()};
                            ^
test.cpp:4:11: note: declared private here
        private: Irritating(const Irritating& other) {}
                 ^
test.cpp:8:29: error: calling a private constructor of class 'Irritating'
const Irritating array[] = {Irritating()};
                            ^
test.cpp:4:11: note: declared private here
        private: Irritating(const Irritating& other) {}
                 ^
1 warning and 1 error generated.
[holt@Michaela irritating]$
4

4 に答える 4

4

個々の配列要素は、=構文で指定された初期化子からのコピー初期化によって初期化されるためです。{...}8.5/12 (C++03) を参照

引数の受け渡し、関数の戻り、例外のスロー (15.1)、例外の処理 (15.3)、および中括弧で囲まれた初期化子リスト (8.5.1) で発生する初期化は、コピー初期化と呼ばれます。

コピー初期化には、コピー コンストラクターが必要です (実際には使用しない場合でも)。

実際には、コピー コンストラクターを公開してコードをコンパイルすると、コンパイラーは、コピー コンストラクターを使用せずに、配列要素をその場で初期化することになる可能性があります。それにもかかわらず、抽象言語の正式な規則では、このコンテキストでのコピー初期化が必要です。

于 2013-01-27T01:43:52.757 に答える
1
class Irritating
{
    public:  Irritating() {}
    private: Irritating(const Irritating& other) {}
};

enum DefaultConstruct { defaultConstruct };

class MaybeTooClever
    : public Irritating
{
public:
    MaybeTooClever( DefaultConstruct = defaultConstruct ) {}
#ifdef __GNUC__
public:
    MaybeTooClever( MaybeTooClever const& other );      // No such.
#else
private:
    MaybeTooClever( MaybeTooClever const& other );      // No such.
#endif
};    

static MaybeTooClever const array[] = { defaultConstruct };

int main()
{}
于 2013-01-27T01:55:05.183 に答える
1

のコピーコンストラクターIrritatingが無効になっていると仮定すると、参照によってそれらを管理するのがおそらく最善の方法よりもコストがかかるためです。

vector<unique_ptr<Irritating>> V = { new Irritating(), ... };

使用パターンに応じてshared_ptr代わりに使用できます。unique_ptr

(Irritating を変更できる場合は、Move コンストラクターを与えることができます。Move セマンティクスを見てください)

それらを適切な場所に構築したい場合は、それらのストレージの配列を作成してから新しいそれらを適切な場所に配置するために使用できますaligned_storage。これにより、元のリクエストでやりたいこととほぼ同じコンパイル済みコードが生成されますが、少し面倒です。

aligned_storage <sizeof(Irritating), alignment_of<Irritating>::value>::type data[N];
new ((Irritating*) data+0) Irritating(...);
new ((Irritating*) data+1) Irritating(...);
new ((Irritating*) data+2) Irritating(...);
...
new ((Irritating*) data+N-1) Irritating(...);

(プログラムの終了時にそれらを削除することを忘れないでください。)

于 2013-01-27T02:01:27.947 に答える
-4

サイズで配列を作成しようとしましたか?デフォルトの ctor を呼び出す必要があります。

このソースにあるように

struct foo {
   int x;
   foo():x(1) {}
private:
   foo( foo const& ) {}
};

foo array[10];

#include <iostream>
int main() {
   for (auto&& i:array) {
      std::cout << i.x << "\n";
   }
}

fooこれは、デフォルトのコピーコンストラクターを持たない配列で初期化されたことを示しています。

あなたの問題が、fooデフォルト以外のコンストラクターで実際に構築したいということである場合、これも同様に行うことができますが、それははるかに困難であり、それはあなたの質問が求めたものではありません. いずれにせよ、要素の配置された構築をサポートする配列のような構造を作成するために必要なものの、非常に大まかなスケッチを次に示します。完成やコンパイルにはほど遠いですが、基本的なテクニックはしっかりしている必要があります。

#include <cstddef>
#include <utility>
#include <type_traits>


template<typename... Args>
struct types {};

template<typename types, typename=void>
struct emplacer;

template<typename T>
struct remove_refref {
  typedef T type;
};
template<typename T>
struct remove_refref<T&&> {
  typedef T type; 
};

template<typename A1, typename... Args>
struct emplacer< types<A1, Args...>, void>:
  emplacer< types<Args...> >
{
  typename remove_refref<A1>::type val;
  emplacer( A1 arg, Args... args ):
    emplacer< types<Args...>, index+1 >( std::forward(args)... ),
    val( std::forward(arg) )
  {}
};

template< std::size_t n >
struct extract {
  template< typename A1, typename... Args >
  A1&& from( emplacer<types<A1, Args...>&& e ) {
    return extract<n-1>::from( emplacer<types<Args...>>&&(e) );
  }
};
template<>
struct extract<0> {
  template< typename A1, typename... Args >
  A1&& from( emplacer<types<A1, Args...>&& e ) {
    return std::move( e.val );
  }
};

template<std::size_t... v>
struct seq {};
template<std::size_t n, std::size_t... tail>
struct make_seq: make_seq<n-1, n-1, tail...> {};
template<std::size_t n, std::size_t... tail>
struct make_seq<0, tail...> {
  typedef seq<tail...> type;
  type val() { return type(); }
};
struct nothing {};
template<typename T, typename... Args, std::size_t... indexes>
nothing construct( T* src, emplacer<types<Args...>>&& e, seq<indexes...> s = make_seq< sizeof...(Args) >::val() ) {
  new(src)T( std::move( extract<indexes>( std::move(e) ))... );
}

template<typename... Args>
emplacer< types<Args...> > emplace( Args&&... a ) {
  return emplacer< types<Args...> >( std::forward(a)... );
}

template<typename T, std::size_t n>
struct my_array {
private:
  union T_mem {
    T t;
    char x;
    T_mem():x(0) {}
  };
  T_mem buff[n];
  template<typename... nothings>
  void do_nothing( nothings...&& ) {}
  template<typename... emplacers, std::size_t... indexes>
  my_array( emplacers&&... em, seq<indexes...> s=make_seq< sizeof...(emplacers) >::val() ) {
    do_nothing( construct( &buff[indexes].t, em)... );
  }
  ~my_array() {
    for( auto&& v:buff) {
      v.t.~T();
    }
  }
  T& operator[](std::size_t n) { return buff[n].t; }
  // etc
};

アイデアは、実際には toと a のunion両方の配列である構造のような配列を作成することです。したがって、適切なアライメントなどを維持しながら、実際に を構築することを避けます。自明でないアラインメントがないと仮定すると、結果のバッファは.TcharTcharT[]

次にemplacer、任意の構築引数のパッケージとして機能する構築引数オブジェクトを取得します。煩わしい理由から、これらのオブジェクトはパラメータの一部の一時的なコピーを作成する必要があります (寿命の問題を回避する方法がわかりません...何か不足している可能性があります)。

のコンストラクターは、my_array任意の数の を受け取り、それらの引数に基づいてemplacersの内容を構築します。buff

次のような配列を作成します。

my_array< Foo, 10 > arr = {
  emplacer( a, b, c ),
  emplacer( x, y, z ),
  ...
};

もう少し作業を行うと、配列内の初期化されていないオブジェクトのデフォルトの構築が可能になります。

しかし、これを正しく書くのは本当に難しいです。

于 2013-01-27T01:41:04.820 に答える