web-dev-qa-db-de.com

Wie rufe ich :: std :: make_shared für eine Klasse mit nur geschützten oder privaten Konstruktoren auf?

Ich habe diesen Code, der nicht funktioniert, aber ich denke, die Absicht ist klar:

testmakeshared.cpp

#include <memory>

class A {
 public:
   static ::std::shared_ptr<A> create() {
      return ::std::make_shared<A>();
   }

 protected:
   A() {}
   A(const A &) = delete;
   const A &operator =(const A &) = delete;
};

::std::shared_ptr<A> foo()
{
   return A::create();
}

Ich erhalte diesen Fehler, wenn ich ihn kompiliere:

g++ -std=c++0x -march=native -mtune=native -O3 -Wall testmakeshared.cpp
In file included from /usr/lib/gcc/x86_64-redhat-linux/4.6.1/../../../../include/c++/4.6.1/bits/shared_ptr.h:52:0,
                 from /usr/lib/gcc/x86_64-redhat-linux/4.6.1/../../../../include/c++/4.6.1/memory:86,
                 from testmakeshared.cpp:1:
testmakeshared.cpp: In constructor ‘std::_Sp_counted_ptr_inplace<_Tp, _Alloc, _Lp>::_Sp_counted_ptr_inplace(_Alloc) [with _Tp = A, _Alloc = std::allocator<A>, __gnu_cxx::_Lock_policy _Lp = (__gnu_cxx::_Lock_policy)2u]’:
/usr/lib/gcc/x86_64-redhat-linux/4.6.1/../../../../include/c++/4.6.1/bits/shared_ptr_base.h:518:8:   instantiated from ‘std::__shared_count<_Lp>::__shared_count(std::_Sp_make_shared_tag, _Tp*, const _Alloc&, _Args&& ...) [with _Tp = A, _Alloc = std::allocator<A>, _Args = {}, __gnu_cxx::_Lock_policy _Lp = (__gnu_cxx::_Lock_policy)2u]’
/usr/lib/gcc/x86_64-redhat-linux/4.6.1/../../../../include/c++/4.6.1/bits/shared_ptr_base.h:986:35:   instantiated from ‘std::__shared_ptr<_Tp, _Lp>::__shared_ptr(std::_Sp_make_shared_tag, const _Alloc&, _Args&& ...) [with _Alloc = std::allocator<A>, _Args = {}, _Tp = A, __gnu_cxx::_Lock_policy _Lp = (__gnu_cxx::_Lock_policy)2u]’
/usr/lib/gcc/x86_64-redhat-linux/4.6.1/../../../../include/c++/4.6.1/bits/shared_ptr.h:313:64:   instantiated from ‘std::shared_ptr<_Tp>::shared_ptr(std::_Sp_make_shared_tag, const _Alloc&, _Args&& ...) [with _Alloc = std::allocator<A>, _Args = {}, _Tp = A]’
/usr/lib/gcc/x86_64-redhat-linux/4.6.1/../../../../include/c++/4.6.1/bits/shared_ptr.h:531:39:   instantiated from ‘std::shared_ptr<_Tp> std::allocate_shared(const _Alloc&, _Args&& ...) [with _Tp = A, _Alloc = std::allocator<A>, _Args = {}]’
/usr/lib/gcc/x86_64-redhat-linux/4.6.1/../../../../include/c++/4.6.1/bits/shared_ptr.h:547:42:   instantiated from ‘std::shared_ptr<_Tp1> std::make_shared(_Args&& ...) [with _Tp = A, _Args = {}]’
testmakeshared.cpp:6:40:   instantiated from here
testmakeshared.cpp:10:8: error: ‘A::A()’ is protected
/usr/lib/gcc/x86_64-redhat-linux/4.6.1/../../../../include/c++/4.6.1/bits/shared_ptr_base.h:400:2: error: within this context

Compilation exited abnormally with code 1 at Tue Nov 15 07:32:58

Diese Nachricht besagt im Wesentlichen, dass einige zufällige Methoden in ::std::make_shared im Template-Instantiierungsstack nicht auf den Konstruktor zugreifen können, da er geschützt ist.

Aber ich möchte wirklich beide ::std::make_shared verwenden und verhindern, dass jemand ein Objekt dieser Klasse erstellt, auf das kein ::std::shared_ptr zeigt. Gibt es eine Möglichkeit, dies zu erreichen?

142
Omnifarious

Diese Antwort ist wahrscheinlich besser und die, die ich wahrscheinlich annehmen werde. Aber ich habe auch eine Methode entwickelt, die hässlicher ist, aber trotzdem immer noch alles inline ist und keine abgeleitete Klasse erfordert:

#include <memory>
#include <string>

class A {
 protected:
   struct this_is_private;

 public:
   explicit A(const this_is_private &) {}
   A(const this_is_private &, ::std::string, int) {}

   template <typename... T>
   static ::std::shared_ptr<A> create(T &&...args) {
      return ::std::make_shared<A>(this_is_private{0},
                                   ::std::forward<T>(args)...);
   }

 protected:
   struct this_is_private {
       explicit this_is_private(int) {}
   };

   A(const A &) = delete;
   const A &operator =(const A &) = delete;
};

::std::shared_ptr<A> foo()
{
   return A::create();
}

::std::shared_ptr<A> bar()
{
   return A::create("George", 5);
}

::std::shared_ptr<A> errors()
{
   ::std::shared_ptr<A> retval;

   // Each of these assignments to retval properly generates errors.
   retval = A::create("George");
   retval = new A(A::this_is_private{0});
   return ::std::move(retval);
}

Edit 2017-01-06: Ich habe dies geändert, um klar zu machen, dass diese Idee für Konstrukteure, die Argumente annehmen, eindeutig und einfach erweiterbar ist, weil andere Leute Antworten in diese Richtung gaben und verwirrt zu sein schienen.

97
Omnifarious

Betrachten Sie die Anforderungen für std::make_shared in 20.7.2.2.6 shared_ptr creation [util.smartptr.shared.create], Absatz 1:

Erfordert: Der Ausdruck ::new (pv) T(std::forward<Args>(args)...), wobei pv den Typ void* hat und auf einen Speicher verweist, der geeignet ist, um ein Objekt des Typs T aufzunehmen, muss formuliert sein. A muss ein Allokator sein (17.6.3.5). Der Copy-Konstruktor und Destruktor von A darf keine Ausnahmen auslösen.

Da die Anforderung in Bezug auf diesen Ausdruck unbedingt festgelegt ist und Dinge wie der Umfang nicht berücksichtigt werden, denke ich, dass Tricks wie Freundschaft richtig sind.

Eine einfache Lösung ist, von A abzuleiten. Dies erfordert nicht, A eine Schnittstelle oder sogar einen polymorphen Typ zu machen.

// interface in header
std::shared_ptr<A> make_a();

// implementation in source
namespace {

struct concrete_A: public A {};

} // namespace

std::shared_ptr<A>
make_a()
{
    return std::make_shared<concrete_A>();
}
64
Luc Danton

Möglicherweise die einfachste Lösung. Basierend auf der vorherigen answer von Mohit Aron und unter Einbeziehung des dlf-Vorschlags.

#include <memory>

class A
{
public:
    static std::shared_ptr<A> create()
    {
        struct make_shared_enabler : public A {};

        return std::make_shared<make_shared_enabler>();
    }

private:
    A() {}  
};
56
Mark Tolley

Hier ist eine nette Lösung dafür:

#include <memory>

class A {
   public:
     static shared_ptr<A> Create();

   private:
     A() {}

     struct MakeSharedEnabler;   
 };

struct A::MakeSharedEnabler : public A {
    MakeSharedEnabler() : A() {
    }
};

shared_ptr<A> A::Create() {
    return make_shared<MakeSharedEnabler>();
}
23
Mohit Aron

Wie wäre es damit?

static std::shared_ptr<A> create()
{
    std::shared_ptr<A> pA(new A());
    return pA;
}
10
Sean
struct A {
public:
  template<typename ...Arg> std::shared_ptr<A> static create(Arg&&...arg) {
    struct EnableMakeShared : public A {
      EnableMakeShared(Arg&&...arg) :A(std::forward<Arg>(arg)...) {}
    };
    return std::make_shared<EnableMakeShared>(std::forward<Arg>(arg)...);
  }
  void dump() const {
    std::cout << a_ << std::endl;
  }
private:
  A(int a) : a_(a) {}
  A(int i, int j) : a_(i + j) {}
  A(std::string const& a) : a_(a.size()) {}
  int a_;
};
10
alpha

Da ich die bereits bereitgestellten Antworten nicht mochte, entschied ich mich für die Suche und fand eine Lösung, die nicht so generisch ist wie die vorherigen Antworten, aber ich mag sie besser (tm). Rückblickend ist es nicht viel schöner als das von Omnifarius, aber es könnte auch andere Leute geben, die es mögen :)

Dies wurde nicht von mir erfunden, sondern die Idee von Jonathan Wakely (GCC-Entwickler). 

Leider funktioniert es nicht mit allen Compilern, da es auf eine kleine Änderung in der Implementierung von std :: allocate_shared angewiesen ist. Diese Änderung ist jetzt ein Update für die Standardbibliotheken, sodass sie in Zukunft möglicherweise von allen Compilern unterstützt wird. Es funktioniert auf GCC 4.7.

Eine Änderungsanforderung für die C++ - Standardbibliotheksarbeitsgruppe ist hier: http://lwg.github.com/issues/lwg-active.html#2070

Der GCC-Patch mit einer Beispielanwendung ist hier: http://old.nabble.com/Re%3A--v3--Implement-pointer_traits-und-allocator_traits-p31723738.html

Die Lösung basiert auf der Idee, std :: allocate_shared (anstelle von std :: make_shared) mit einem benutzerdefinierten Zuweiser zu verwenden, der der Klasse mit dem privaten Konstruktor als Freund erklärt wird.

Das Beispiel aus dem OP würde so aussehen:

#include <memory>

template<typename Private>
struct MyAlloc : std::allocator<Private>
{
    void construct(void* p) { ::new(p) Private(); }
};

class A {
    public:
        static ::std::shared_ptr<A> create() {
            return ::std::allocate_shared<A>(MyAlloc<A>());
        }

    protected:
        A() {}
        A(const A &) = delete;
        const A &operator =(const A &) = delete;

        friend struct MyAlloc<A>;
};

int main() {
    auto p = A::create();
    return 0;
}

Ein komplexeres Beispiel, das auf dem Dienstprogramm basiert, an dem ich arbeite. Damit konnte ich Lucs Lösung nicht verwenden. Aber die von Omnifarius könnte angepasst werden. Obwohl im vorherigen Beispiel nicht jeder mit dem MyAlloc-Objekt ein A-Objekt erstellen kann, gibt es keine Möglichkeit, A oder B neben der create () - Methode zu erstellen.

#include <memory>

template<typename T>
class safe_enable_shared_from_this : public std::enable_shared_from_this<T>
{
    public:
    template<typename... _Args>
        static ::std::shared_ptr<T> create(_Args&&... p_args) {
            return ::std::allocate_shared<T>(Alloc(), std::forward<_Args>(p_args)...);
        }

    protected:
    struct Alloc : std::allocator<T>
    {  
        template<typename _Up, typename... _Args>
        void construct(_Up* __p, _Args&&... __args)
        { ::new((void *)__p) _Up(std::forward<_Args>(__args)...); }
    };
    safe_enable_shared_from_this(const safe_enable_shared_from_this&) = delete;
    safe_enable_shared_from_this& operator=(const safe_enable_shared_from_this&) = delete;
};

class A : public safe_enable_shared_from_this<A> {
    private:
        A() {}
        friend struct safe_enable_shared_from_this<A>::Alloc;
};

class B : public safe_enable_shared_from_this<B> {
    private:
        B(int v) {}
        friend struct safe_enable_shared_from_this<B>::Alloc;
};

int main() {
    auto a = A::create();
    auto b = B::create(5);
    return 0;
}
8
Zsolt Rizsányi

Ich weiß, dass dieser Thread ziemlich alt ist, aber ich habe eine Antwort gefunden, die keine Vererbung oder zusätzliche Argumente für den Konstruktor erfordert, die ich anderswo nicht sehen konnte. Es ist nicht tragbar:

#include <memory>

#if defined(__cplusplus) && __cplusplus >= 201103L
#define ALLOW_MAKE_SHARED(x) friend void __gnu_cxx::new_allocator<test>::construct<test>(test*);
#Elif defined(_WIN32) || defined(WIN32)
#if defined(_MSC_VER) && _MSC_VER >= 1800
#define ALLOW_MAKE_SHARED(x) friend class std::_Ref_count_obj;
#else
#error msc version does not suport c++11
#endif
#else
#error implement for platform
#endif

class test {
    test() {}
    ALLOW_MAKE_SHARED(test);
public:
    static std::shared_ptr<test> create() { return std::make_shared<test>(); }

};
int main() {
    std::shared_ptr<test> t(test::create());
}

Ich habe unter Windows und Linux getestet, es muss möglicherweise für verschiedene Plattformen angepasst werden.

3
ashleysmithgpu

Idealerweise denke ich, dass die perfekte Lösung Ergänzungen des C++ - Standards erfordern würde. Andrew Schepler schlägt Folgendes vor:

(Geh hier für den ganzen Thread)

wir können eine Idee von boost :: iterator_core_access ausleihen. Ich schlage vor eine neue Klasse std::shared_ptr_access ohne public oder geschützte Mitglieder, und um dies für .__ anzugeben. std :: make_shared (args ...) und std :: alloc_shared (a, args ...), die Die Ausdrücke :: new (pv) T(forward(args)...) und ptr-> ~ T () müssen .__ sein. gut geformt im Kontext von std :: shared_ptr_access.

Eine Implementierung von std :: shared_ptr_access könnte folgendermaßen aussehen:

namespace std {
    class shared_ptr_access
    {
        template <typename _T, typename ... _Args>
        static _T* __construct(void* __pv, _Args&& ... __args)
        { return ::new(__pv) _T(forward<_Args>(__args)...); }

        template <typename _T>
        static void __destroy(_T* __ptr) { __ptr->~_T(); }

        template <typename _T, typename _A>
        friend class __shared_ptr_storage;
    };
}

Verwendungszweck

Wenn das obige zum Standard hinzugefügt wird, würden wir einfach Folgendes tun:

class A {
public:
   static std::shared_ptr<A> create() {
      return std::make_shared<A>();
   }

 protected:
   friend class std::shared_ptr_access;
   A() {}
   A(const A &) = delete;
   const A &operator =(const A &) = delete;
};

Wenn dies für Sie auch nach einer wichtigen Ergänzung des Standards klingt, fügen Sie der verknüpften isocpp Google Group Ihre 2 Cent hinzu.

3
Boris

Wenn Sie auch einen Konstruktor aktivieren möchten, der Argumente akzeptiert, kann dies etwas helfen.

#include <memory>
#include <utility>

template<typename S>
struct enable_make : public S
{
    template<typename... T>
    enable_make(T&&... t)
        : S(std::forward<T>(t)...)
    {
    }
};

class foo
{
public:
    static std::unique_ptr<foo> create(std::unique_ptr<int> u, char const* s)
    {
        return std::make_unique<enable_make<foo>>(std::move(u), s);
    }
protected:
    foo(std::unique_ptr<int> u, char const* s)
    {
    }
};

void test()
{
    auto fp = foo::create(std::make_unique<int>(3), "asdf");
}
2
demo

[Bearbeiten] Ich habe den oben angegebenen Thread auf einem standardisierten std::shared_ptr_access<>-Vorschlag durchgelesen. Dort gab es eine Antwort, in der ein Fix auf std::allocate_shared<> und ein Beispiel für seine Verwendung festgehalten wurde. Ich habe es unten an eine Factory-Vorlage angepasst und unter gcc C++ 11/14/17 getestet. Es funktioniert auch mit std::enable_shared_from_this<>, wäre also meiner ursprünglichen Lösung in dieser Antwort offensichtlich vorzuziehen. Hier ist es...

#include <iostream>
#include <memory>

class Factory final {
public:
    template<typename T, typename... A>
    static std::shared_ptr<T> make_shared(A&&... args) {
        return std::allocate_shared<T>(Alloc<T>(), std::forward<A>(args)...);
    }
private:
    template<typename T>
    struct Alloc : std::allocator<T> {
        template<typename U, typename... A>
        void construct(U* ptr, A&&... args) {
            new(ptr) U(std::forward<A>(args)...);
        }
        template<typename U>
        void destroy(U* ptr) {
            ptr->~U();
        }
    };  
};

class X final : public std::enable_shared_from_this<X> {
    friend class Factory;
private:
    X()      { std::cout << "X() addr=" << this << "\n"; }
    X(int i) { std::cout << "X(int) addr=" << this << " i=" << i << "\n"; }
    ~X()     { std::cout << "~X()\n"; }
};

int main() {
    auto p1 = Factory::make_shared<X>(42);
    auto p2 = p1->shared_from_this();
    std::cout << "p1=" << p1 << "\n"
              << "p2=" << p2 << "\n"
              << "count=" << p1.use_count() << "\n";
}

[Orig] Ich habe mit dem Aliasing-Konstruktor für gemeinsam genutzte Zeiger eine Lösung gefunden. Damit können sowohl der ctor als auch der dtor privat sein und der endgültige Bezeichner verwendet werden.

#include <iostream>
#include <memory>

class Factory final {
public:
    template<typename T, typename... A>
    static std::shared_ptr<T> make_shared(A&&... args) {
        auto ptr = std::make_shared<Type<T>>(std::forward<A>(args)...);
        return std::shared_ptr<T>(ptr, &ptr->type);
    }
private:
    template<typename T>
    struct Type final {
        template<typename... A>
        Type(A&&... args) : type(std::forward<A>(args)...) { std::cout << "Type(...) addr=" << this << "\n"; }
        ~Type() { std::cout << "~Type()\n"; }
        T type;
    };
};

class X final {
    friend struct Factory::Type<X>;  // factory access
private:
    X()      { std::cout << "X() addr=" << this << "\n"; }
    X(int i) { std::cout << "X(...) addr=" << this << " i=" << i << "\n"; }
    ~X()     { std::cout << "~X()\n"; }
};

int main() {
    auto ptr1 = Factory::make_shared<X>();
    auto ptr2 = Factory::make_shared<X>(42);
}

Beachten Sie, dass der oben beschriebene Ansatz mit std::enable_shared_from_this<> nicht gut funktioniert, da der ursprüngliche std::shared_ptr<> für den Wrapper und nicht der Typ selbst gilt. Wir können dies mit einer gleichwertigen Klasse ansprechen, die mit der Fabrik kompatibel ist ...

#include <iostream>
#include <memory>

template<typename T>
class EnableShared {
    friend class Factory;  // factory access
public:
    std::shared_ptr<T> shared_from_this() { return weak.lock(); }
protected:
    EnableShared() = default;
    virtual ~EnableShared() = default;
    EnableShared<T>& operator=(const EnableShared<T>&) { return *this; }  // no slicing
private:
    std::weak_ptr<T> weak;
};

class Factory final {
public:
    template<typename T, typename... A>
    static std::shared_ptr<T> make_shared(A&&... args) {
        auto ptr = std::make_shared<Type<T>>(std::forward<A>(args)...);
        auto alt = std::shared_ptr<T>(ptr, &ptr->type);
        assign(std::is_base_of<EnableShared<T>, T>(), alt);
        return alt;
    }
private:
    template<typename T>
    struct Type final {
        template<typename... A>
        Type(A&&... args) : type(std::forward<A>(args)...) { std::cout << "Type(...) addr=" << this << "\n"; }
        ~Type() { std::cout << "~Type()\n"; }
        T type;
    };
    template<typename T>
    static void assign(std::true_type, const std::shared_ptr<T>& ptr) {
        ptr->weak = ptr;
    }
    template<typename T>
    static void assign(std::false_type, const std::shared_ptr<T>&) {}
};

class X final : public EnableShared<X> {
    friend struct Factory::Type<X>;  // factory access
private:
    X()      { std::cout << "X() addr=" << this << "\n"; }
    X(int i) { std::cout << "X(...) addr=" << this << " i=" << i << "\n"; }
    ~X()     { std::cout << "~X()\n"; }
};

int main() {
    auto ptr1 = Factory::make_shared<X>();
    auto ptr2 = ptr1->shared_from_this();
    std::cout << "ptr1=" << ptr1.get() << "\nptr2=" << ptr2.get() << "\n";
}

Schließlich sagte jemand, dass Clang sich beschwert hat, dass Factory :: Type privat ist, wenn er als Freund verwendet wird. Das Freilegen schadet nicht.

2
user1715587

Es gibt ein haarigeres und interessanteres Problem, das auftritt, wenn zwei streng miteinander verwandte Klassen A und B zusammenarbeiten.

Angenommen, A ist die "Meisterklasse" und B sein "Slave". Wenn Sie die Instantiierung von B nur auf A beschränken möchten, würden Sie den Konstruktor von B als privat und Freund B auf A wie folgt festlegen

class B
{
public:
    // B your methods...

private:
    B();
    friend class A;
};

Leider führt der Aufruf von std::make_shared<B>() aus einer Methode von A dazu, dass sich der Compiler darüber beschwert, dass B::B() privat ist.

Meine Lösung hierfür ist die Erstellung einer öffentlichen Pass-Dummy-Klasse (genau wie nullptr_t) in B, die über einen privaten Konstruktor verfügt und mit A befreundet ist und den Konstruktor von B öffentlich macht und den Argumenten Pass so hinzufügt.

class B
{
public:
  class Pass
  {
    Pass() {}
    friend class A;
  };

  B(Pass, int someArgument)
  {
  }
};

class A
{
public:
  A()
  {
    // This is valid
    auto ptr = std::make_shared<B>(B::Pass(), 42);
  }
};

class C
{
public:
  C()
  {
    // This is not
    auto ptr = std::make_shared<B>(B::Pass(), 42);
  }
};
1
keebus

Ich hatte das gleiche Problem, aber keine der vorhandenen Antworten war wirklich zufriedenstellend, da ich Argumente an den geschützten Konstruktor übergeben muss. Darüber hinaus muss ich dies für mehrere Klassen tun, die jeweils unterschiedliche Argumente verwenden.

In diesem Sinne und aufbauend auf einigen der vorhandenen Antworten, die alle ähnliche Methoden verwenden, stelle ich dieses kleine Nugget vor:

template < typename Object, typename... Args >
inline std::shared_ptr< Object >
protected_make_shared( Args&&... args )
{
  struct helper : public Object
  {
    helper( Args&&... args )
      : Object{ std::forward< Args >( args )... }
    {}
  };

  return std::make_shared< helper >( std::forward< Args >( args )... );
}
0
Matthew

Die Wurzel des Problems besteht darin, dass, wenn die Funktion oder Klasse, die Sie befreundet haben, Aufrufe Ihres Konstruktors auf niedriger Ebene durchführt, diese ebenfalls befreundet sein müssen. std :: make_shared ist nicht die Funktion, die Ihren Konstruktor tatsächlich aufruft. Daher macht Freundschaften keinen Unterschied.

class A;
typedef std::shared_ptr<A> APtr;
class A
{
    template<class T>
    friend class std::_Ref_count_obj;
public:
    APtr create()
    {
        return std::make_shared<A>();
    }
private:
    A()
    {}
};

std :: _ Ref_count_obj ruft tatsächlich Ihren Konstruktor auf, also muss es ein Freund sein. Da das ein wenig unklar ist, verwende ich ein Makro

#define SHARED_PTR_DECL(T) \
class T; \
typedef std::shared_ptr<T> ##T##Ptr;

#define FRIEND_STD_MAKE_SHARED \
template<class T> \
friend class std::_Ref_count_obj;

Dann sieht Ihre Klassendeklaration recht einfach aus. Sie können ein einzelnes Makro erstellen, um den ptr und die Klasse zu deklarieren, wenn Sie möchten.

SHARED_PTR_DECL(B);
class B
{
    FRIEND_STD_MAKE_SHARED
public:
    BPtr create()
    {
        return std::make_shared<B>();
    }
private:
    B()
    {}
};

Dies ist eigentlich ein wichtiges Thema ... Um tragbaren Code für die Wartung zu erhalten, müssen Sie die Implementierung so weit wie möglich ausblenden.

typedef std::shared_ptr<A> APtr;

verbirgt die Art und Weise, wie Sie mit Ihrem intelligenten Zeiger umgehen, Sie müssen sicher sein, dass Sie Ihre Typedef verwenden. Wenn Sie jedoch immer eine Anwendung mit make_shared erstellen müssen, ist der Zweck nicht erfüllt.

Das obige Beispiel erzwingt, dass der Code für Ihre Klasse den Smart-Pointer-Konstruktor verwendet. Wenn Sie also zu einem neuen Smart-Pointer wechseln, ändern Sie Ihre Klassendeklaration und haben eine gute Chance, fertig zu sein. Gehen Sie NICHT davon aus, dass Ihr nächster Chef oder Ihr nächstes Projekt einen stl, boost usw.-Plan verwendet, um ihn eines Tages zu ändern.

Seit fast 30 Jahren habe ich einen hohen Preis für Zeit, Schmerz und Nebenwirkungen bezahlt, um dies zu beheben, wenn es vor Jahren falsch gemacht wurde.

0
brtip