std::tuple with non moveable/copyable type











up vote
2
down vote

favorite












Assume I have a struct MyStruct which contains a std::mutex and only reference or pod members. I want to create a std::tuple containing this type. But as MyStruct is not copy- or moveable it doen't work. It is working if I add a move constructor in the following way:



#include <tuple>
#include <mutex>
#include <vector>

struct MyStruct {
std::vector<double>& vec_;
int number_;
std::mutex mutex_;

MyStruct(std::vector<double>& vec, const int number)
: vec_(vec), number_(number) {}
MyStruct(MyStruct&& o) : vec_(o.vec_), number_(o.number_)
{
std::lock_guard guard(o.mutex_);
}
};

int main()
{
using T = std::tuple<MyStruct,int>;

std::vector<double> v{1., 2., 3.};
T t{MyStruct{v, 1}, 2};
}


Is this safe? In the end I only want to construct it inside the std::tuple before any multi-threading is going on, but how can I ensure that the elements of the std::tuple are not moved once the std::tuple is constructed? Or is there a way to construct a std::tuple in place?










share|improve this question






















  • afaik, you can construct a pair in-place, not a tuple
    – Piotr Skotnicki
    Nov 19 at 12:54










  • Won't auto tuple = std::make_tuple<MyStruct, int>({v, 1}, 2); work?
    – Ted Lyngmo
    Nov 19 at 12:59










  • Not with gcc 8.2.0.
    – Max
    Nov 19 at 13:01










  • g++ 8.2.1 accepts it - but I'm not sure it's really constructed in-place. Btw, do you really mean to have a reference to the vector of doubles?
    – Ted Lyngmo
    Nov 19 at 13:04












  • Also, moving a mutex is ok, so you could probably just delete the copy ctor and copy assignment operator and make the move ctor and move assignment default.
    – Ted Lyngmo
    Nov 19 at 13:11

















up vote
2
down vote

favorite












Assume I have a struct MyStruct which contains a std::mutex and only reference or pod members. I want to create a std::tuple containing this type. But as MyStruct is not copy- or moveable it doen't work. It is working if I add a move constructor in the following way:



#include <tuple>
#include <mutex>
#include <vector>

struct MyStruct {
std::vector<double>& vec_;
int number_;
std::mutex mutex_;

MyStruct(std::vector<double>& vec, const int number)
: vec_(vec), number_(number) {}
MyStruct(MyStruct&& o) : vec_(o.vec_), number_(o.number_)
{
std::lock_guard guard(o.mutex_);
}
};

int main()
{
using T = std::tuple<MyStruct,int>;

std::vector<double> v{1., 2., 3.};
T t{MyStruct{v, 1}, 2};
}


Is this safe? In the end I only want to construct it inside the std::tuple before any multi-threading is going on, but how can I ensure that the elements of the std::tuple are not moved once the std::tuple is constructed? Or is there a way to construct a std::tuple in place?










share|improve this question






















  • afaik, you can construct a pair in-place, not a tuple
    – Piotr Skotnicki
    Nov 19 at 12:54










  • Won't auto tuple = std::make_tuple<MyStruct, int>({v, 1}, 2); work?
    – Ted Lyngmo
    Nov 19 at 12:59










  • Not with gcc 8.2.0.
    – Max
    Nov 19 at 13:01










  • g++ 8.2.1 accepts it - but I'm not sure it's really constructed in-place. Btw, do you really mean to have a reference to the vector of doubles?
    – Ted Lyngmo
    Nov 19 at 13:04












  • Also, moving a mutex is ok, so you could probably just delete the copy ctor and copy assignment operator and make the move ctor and move assignment default.
    – Ted Lyngmo
    Nov 19 at 13:11















up vote
2
down vote

favorite









up vote
2
down vote

favorite











Assume I have a struct MyStruct which contains a std::mutex and only reference or pod members. I want to create a std::tuple containing this type. But as MyStruct is not copy- or moveable it doen't work. It is working if I add a move constructor in the following way:



#include <tuple>
#include <mutex>
#include <vector>

struct MyStruct {
std::vector<double>& vec_;
int number_;
std::mutex mutex_;

MyStruct(std::vector<double>& vec, const int number)
: vec_(vec), number_(number) {}
MyStruct(MyStruct&& o) : vec_(o.vec_), number_(o.number_)
{
std::lock_guard guard(o.mutex_);
}
};

int main()
{
using T = std::tuple<MyStruct,int>;

std::vector<double> v{1., 2., 3.};
T t{MyStruct{v, 1}, 2};
}


Is this safe? In the end I only want to construct it inside the std::tuple before any multi-threading is going on, but how can I ensure that the elements of the std::tuple are not moved once the std::tuple is constructed? Or is there a way to construct a std::tuple in place?










share|improve this question













Assume I have a struct MyStruct which contains a std::mutex and only reference or pod members. I want to create a std::tuple containing this type. But as MyStruct is not copy- or moveable it doen't work. It is working if I add a move constructor in the following way:



#include <tuple>
#include <mutex>
#include <vector>

struct MyStruct {
std::vector<double>& vec_;
int number_;
std::mutex mutex_;

MyStruct(std::vector<double>& vec, const int number)
: vec_(vec), number_(number) {}
MyStruct(MyStruct&& o) : vec_(o.vec_), number_(o.number_)
{
std::lock_guard guard(o.mutex_);
}
};

int main()
{
using T = std::tuple<MyStruct,int>;

std::vector<double> v{1., 2., 3.};
T t{MyStruct{v, 1}, 2};
}


Is this safe? In the end I only want to construct it inside the std::tuple before any multi-threading is going on, but how can I ensure that the elements of the std::tuple are not moved once the std::tuple is constructed? Or is there a way to construct a std::tuple in place?







c++






share|improve this question













share|improve this question











share|improve this question




share|improve this question










asked Nov 19 at 12:42









Max

242112




242112












  • afaik, you can construct a pair in-place, not a tuple
    – Piotr Skotnicki
    Nov 19 at 12:54










  • Won't auto tuple = std::make_tuple<MyStruct, int>({v, 1}, 2); work?
    – Ted Lyngmo
    Nov 19 at 12:59










  • Not with gcc 8.2.0.
    – Max
    Nov 19 at 13:01










  • g++ 8.2.1 accepts it - but I'm not sure it's really constructed in-place. Btw, do you really mean to have a reference to the vector of doubles?
    – Ted Lyngmo
    Nov 19 at 13:04












  • Also, moving a mutex is ok, so you could probably just delete the copy ctor and copy assignment operator and make the move ctor and move assignment default.
    – Ted Lyngmo
    Nov 19 at 13:11




















  • afaik, you can construct a pair in-place, not a tuple
    – Piotr Skotnicki
    Nov 19 at 12:54










  • Won't auto tuple = std::make_tuple<MyStruct, int>({v, 1}, 2); work?
    – Ted Lyngmo
    Nov 19 at 12:59










  • Not with gcc 8.2.0.
    – Max
    Nov 19 at 13:01










  • g++ 8.2.1 accepts it - but I'm not sure it's really constructed in-place. Btw, do you really mean to have a reference to the vector of doubles?
    – Ted Lyngmo
    Nov 19 at 13:04












  • Also, moving a mutex is ok, so you could probably just delete the copy ctor and copy assignment operator and make the move ctor and move assignment default.
    – Ted Lyngmo
    Nov 19 at 13:11


















afaik, you can construct a pair in-place, not a tuple
– Piotr Skotnicki
Nov 19 at 12:54




afaik, you can construct a pair in-place, not a tuple
– Piotr Skotnicki
Nov 19 at 12:54












Won't auto tuple = std::make_tuple<MyStruct, int>({v, 1}, 2); work?
– Ted Lyngmo
Nov 19 at 12:59




Won't auto tuple = std::make_tuple<MyStruct, int>({v, 1}, 2); work?
– Ted Lyngmo
Nov 19 at 12:59












Not with gcc 8.2.0.
– Max
Nov 19 at 13:01




Not with gcc 8.2.0.
– Max
Nov 19 at 13:01












g++ 8.2.1 accepts it - but I'm not sure it's really constructed in-place. Btw, do you really mean to have a reference to the vector of doubles?
– Ted Lyngmo
Nov 19 at 13:04






g++ 8.2.1 accepts it - but I'm not sure it's really constructed in-place. Btw, do you really mean to have a reference to the vector of doubles?
– Ted Lyngmo
Nov 19 at 13:04














Also, moving a mutex is ok, so you could probably just delete the copy ctor and copy assignment operator and make the move ctor and move assignment default.
– Ted Lyngmo
Nov 19 at 13:11






Also, moving a mutex is ok, so you could probably just delete the copy ctor and copy assignment operator and make the move ctor and move assignment default.
– Ted Lyngmo
Nov 19 at 13:11














1 Answer
1






active

oldest

votes

















up vote
1
down vote



accepted










Your code may be safe in particular circumstances, but if you want it to be safe in general, then you need to fix up your move constructor. The move constructor must properly synchronize access to the moved-from object's state, and it must prevent that object from modifying the state once the new object has stolen it:



class MyStruct {
std::vector<double>* vec_; // use a pointer here, not a reference
int number_;
std::mutex mutex_;
public:
MyStruct(std::vector<double>& vec, const int number)
: vec_(&vec), number_(number) {}
MyStruct(MyStruct&& o)
{
std::lock_guard guard(o.mutex_);
vec_ = std::exchange(o.vec_, nullptr);
number_ = std::exchange(o.number_, 0); // doesn't necessarily have to be 0 here
}
};


You will also have to change any other methods to account for the moved-from state. Also note that I've changed this to a class and made the state private. Having a public mutex and public data members invites trouble. For safety, you must ensure that all access to the mutable state is properly synchronized.



Without the Move Constructor



If you want to avoid the move constructor, then the most obvious solution is to avoid using a tuple. tuple is intended for use with generic code in libraries. Valid uses in other contexts exist but are rare. Consider using a struct to store the values instead:



struct Fixture
{
MyStruct a;
int b;
};


This has the advantage of naming your members (which is more readable) and allowing custom constructors.



Using a std::tuple



If you absolutely must use a tuple, then the problem is still solvable. You just need a way to construct your MyStruct object after the tuple is constructed. There a few ways you might do that. Here are two:



Use std::optional:



int main()
{
using T = std::tuple<std::optional<MyStruct>,int>;

std::vector<double> v{1., 2., 3.};
T t{std::nullopt, 2};

std::get<0>(t).emplace(v, 1);
}


Define a default constructor and use two-phase initialization:



// Add a constructor like the following, once again using a pointer instead of reference
MyStruct() : vec_(nullptr), number_(0) {}

// Add an init method
void init(std::vector<double>& vec, int number)
{
vec_ = &vec;
number_ = number;
}

// Later
int main()
{
using T = std::tuple<MyStruct,int>;

std::vector<double> v{1., 2., 3.};
T t{MyStruct{}, 2};

std::get<0>(t).init(v, 1);
}





share|improve this answer























    Your Answer






    StackExchange.ifUsing("editor", function () {
    StackExchange.using("externalEditor", function () {
    StackExchange.using("snippets", function () {
    StackExchange.snippets.init();
    });
    });
    }, "code-snippets");

    StackExchange.ready(function() {
    var channelOptions = {
    tags: "".split(" "),
    id: "1"
    };
    initTagRenderer("".split(" "), "".split(" "), channelOptions);

    StackExchange.using("externalEditor", function() {
    // Have to fire editor after snippets, if snippets enabled
    if (StackExchange.settings.snippets.snippetsEnabled) {
    StackExchange.using("snippets", function() {
    createEditor();
    });
    }
    else {
    createEditor();
    }
    });

    function createEditor() {
    StackExchange.prepareEditor({
    heartbeatType: 'answer',
    convertImagesToLinks: true,
    noModals: true,
    showLowRepImageUploadWarning: true,
    reputationToPostImages: 10,
    bindNavPrevention: true,
    postfix: "",
    imageUploader: {
    brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
    contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
    allowUrls: true
    },
    onDemand: true,
    discardSelector: ".discard-answer"
    ,immediatelyShowMarkdownHelp:true
    });


    }
    });














    draft saved

    draft discarded


















    StackExchange.ready(
    function () {
    StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53374906%2fstdtuple-with-non-moveable-copyable-type%23new-answer', 'question_page');
    }
    );

    Post as a guest















    Required, but never shown

























    1 Answer
    1






    active

    oldest

    votes








    1 Answer
    1






    active

    oldest

    votes









    active

    oldest

    votes






    active

    oldest

    votes








    up vote
    1
    down vote



    accepted










    Your code may be safe in particular circumstances, but if you want it to be safe in general, then you need to fix up your move constructor. The move constructor must properly synchronize access to the moved-from object's state, and it must prevent that object from modifying the state once the new object has stolen it:



    class MyStruct {
    std::vector<double>* vec_; // use a pointer here, not a reference
    int number_;
    std::mutex mutex_;
    public:
    MyStruct(std::vector<double>& vec, const int number)
    : vec_(&vec), number_(number) {}
    MyStruct(MyStruct&& o)
    {
    std::lock_guard guard(o.mutex_);
    vec_ = std::exchange(o.vec_, nullptr);
    number_ = std::exchange(o.number_, 0); // doesn't necessarily have to be 0 here
    }
    };


    You will also have to change any other methods to account for the moved-from state. Also note that I've changed this to a class and made the state private. Having a public mutex and public data members invites trouble. For safety, you must ensure that all access to the mutable state is properly synchronized.



    Without the Move Constructor



    If you want to avoid the move constructor, then the most obvious solution is to avoid using a tuple. tuple is intended for use with generic code in libraries. Valid uses in other contexts exist but are rare. Consider using a struct to store the values instead:



    struct Fixture
    {
    MyStruct a;
    int b;
    };


    This has the advantage of naming your members (which is more readable) and allowing custom constructors.



    Using a std::tuple



    If you absolutely must use a tuple, then the problem is still solvable. You just need a way to construct your MyStruct object after the tuple is constructed. There a few ways you might do that. Here are two:



    Use std::optional:



    int main()
    {
    using T = std::tuple<std::optional<MyStruct>,int>;

    std::vector<double> v{1., 2., 3.};
    T t{std::nullopt, 2};

    std::get<0>(t).emplace(v, 1);
    }


    Define a default constructor and use two-phase initialization:



    // Add a constructor like the following, once again using a pointer instead of reference
    MyStruct() : vec_(nullptr), number_(0) {}

    // Add an init method
    void init(std::vector<double>& vec, int number)
    {
    vec_ = &vec;
    number_ = number;
    }

    // Later
    int main()
    {
    using T = std::tuple<MyStruct,int>;

    std::vector<double> v{1., 2., 3.};
    T t{MyStruct{}, 2};

    std::get<0>(t).init(v, 1);
    }





    share|improve this answer



























      up vote
      1
      down vote



      accepted










      Your code may be safe in particular circumstances, but if you want it to be safe in general, then you need to fix up your move constructor. The move constructor must properly synchronize access to the moved-from object's state, and it must prevent that object from modifying the state once the new object has stolen it:



      class MyStruct {
      std::vector<double>* vec_; // use a pointer here, not a reference
      int number_;
      std::mutex mutex_;
      public:
      MyStruct(std::vector<double>& vec, const int number)
      : vec_(&vec), number_(number) {}
      MyStruct(MyStruct&& o)
      {
      std::lock_guard guard(o.mutex_);
      vec_ = std::exchange(o.vec_, nullptr);
      number_ = std::exchange(o.number_, 0); // doesn't necessarily have to be 0 here
      }
      };


      You will also have to change any other methods to account for the moved-from state. Also note that I've changed this to a class and made the state private. Having a public mutex and public data members invites trouble. For safety, you must ensure that all access to the mutable state is properly synchronized.



      Without the Move Constructor



      If you want to avoid the move constructor, then the most obvious solution is to avoid using a tuple. tuple is intended for use with generic code in libraries. Valid uses in other contexts exist but are rare. Consider using a struct to store the values instead:



      struct Fixture
      {
      MyStruct a;
      int b;
      };


      This has the advantage of naming your members (which is more readable) and allowing custom constructors.



      Using a std::tuple



      If you absolutely must use a tuple, then the problem is still solvable. You just need a way to construct your MyStruct object after the tuple is constructed. There a few ways you might do that. Here are two:



      Use std::optional:



      int main()
      {
      using T = std::tuple<std::optional<MyStruct>,int>;

      std::vector<double> v{1., 2., 3.};
      T t{std::nullopt, 2};

      std::get<0>(t).emplace(v, 1);
      }


      Define a default constructor and use two-phase initialization:



      // Add a constructor like the following, once again using a pointer instead of reference
      MyStruct() : vec_(nullptr), number_(0) {}

      // Add an init method
      void init(std::vector<double>& vec, int number)
      {
      vec_ = &vec;
      number_ = number;
      }

      // Later
      int main()
      {
      using T = std::tuple<MyStruct,int>;

      std::vector<double> v{1., 2., 3.};
      T t{MyStruct{}, 2};

      std::get<0>(t).init(v, 1);
      }





      share|improve this answer

























        up vote
        1
        down vote



        accepted







        up vote
        1
        down vote



        accepted






        Your code may be safe in particular circumstances, but if you want it to be safe in general, then you need to fix up your move constructor. The move constructor must properly synchronize access to the moved-from object's state, and it must prevent that object from modifying the state once the new object has stolen it:



        class MyStruct {
        std::vector<double>* vec_; // use a pointer here, not a reference
        int number_;
        std::mutex mutex_;
        public:
        MyStruct(std::vector<double>& vec, const int number)
        : vec_(&vec), number_(number) {}
        MyStruct(MyStruct&& o)
        {
        std::lock_guard guard(o.mutex_);
        vec_ = std::exchange(o.vec_, nullptr);
        number_ = std::exchange(o.number_, 0); // doesn't necessarily have to be 0 here
        }
        };


        You will also have to change any other methods to account for the moved-from state. Also note that I've changed this to a class and made the state private. Having a public mutex and public data members invites trouble. For safety, you must ensure that all access to the mutable state is properly synchronized.



        Without the Move Constructor



        If you want to avoid the move constructor, then the most obvious solution is to avoid using a tuple. tuple is intended for use with generic code in libraries. Valid uses in other contexts exist but are rare. Consider using a struct to store the values instead:



        struct Fixture
        {
        MyStruct a;
        int b;
        };


        This has the advantage of naming your members (which is more readable) and allowing custom constructors.



        Using a std::tuple



        If you absolutely must use a tuple, then the problem is still solvable. You just need a way to construct your MyStruct object after the tuple is constructed. There a few ways you might do that. Here are two:



        Use std::optional:



        int main()
        {
        using T = std::tuple<std::optional<MyStruct>,int>;

        std::vector<double> v{1., 2., 3.};
        T t{std::nullopt, 2};

        std::get<0>(t).emplace(v, 1);
        }


        Define a default constructor and use two-phase initialization:



        // Add a constructor like the following, once again using a pointer instead of reference
        MyStruct() : vec_(nullptr), number_(0) {}

        // Add an init method
        void init(std::vector<double>& vec, int number)
        {
        vec_ = &vec;
        number_ = number;
        }

        // Later
        int main()
        {
        using T = std::tuple<MyStruct,int>;

        std::vector<double> v{1., 2., 3.};
        T t{MyStruct{}, 2};

        std::get<0>(t).init(v, 1);
        }





        share|improve this answer














        Your code may be safe in particular circumstances, but if you want it to be safe in general, then you need to fix up your move constructor. The move constructor must properly synchronize access to the moved-from object's state, and it must prevent that object from modifying the state once the new object has stolen it:



        class MyStruct {
        std::vector<double>* vec_; // use a pointer here, not a reference
        int number_;
        std::mutex mutex_;
        public:
        MyStruct(std::vector<double>& vec, const int number)
        : vec_(&vec), number_(number) {}
        MyStruct(MyStruct&& o)
        {
        std::lock_guard guard(o.mutex_);
        vec_ = std::exchange(o.vec_, nullptr);
        number_ = std::exchange(o.number_, 0); // doesn't necessarily have to be 0 here
        }
        };


        You will also have to change any other methods to account for the moved-from state. Also note that I've changed this to a class and made the state private. Having a public mutex and public data members invites trouble. For safety, you must ensure that all access to the mutable state is properly synchronized.



        Without the Move Constructor



        If you want to avoid the move constructor, then the most obvious solution is to avoid using a tuple. tuple is intended for use with generic code in libraries. Valid uses in other contexts exist but are rare. Consider using a struct to store the values instead:



        struct Fixture
        {
        MyStruct a;
        int b;
        };


        This has the advantage of naming your members (which is more readable) and allowing custom constructors.



        Using a std::tuple



        If you absolutely must use a tuple, then the problem is still solvable. You just need a way to construct your MyStruct object after the tuple is constructed. There a few ways you might do that. Here are two:



        Use std::optional:



        int main()
        {
        using T = std::tuple<std::optional<MyStruct>,int>;

        std::vector<double> v{1., 2., 3.};
        T t{std::nullopt, 2};

        std::get<0>(t).emplace(v, 1);
        }


        Define a default constructor and use two-phase initialization:



        // Add a constructor like the following, once again using a pointer instead of reference
        MyStruct() : vec_(nullptr), number_(0) {}

        // Add an init method
        void init(std::vector<double>& vec, int number)
        {
        vec_ = &vec;
        number_ = number;
        }

        // Later
        int main()
        {
        using T = std::tuple<MyStruct,int>;

        std::vector<double> v{1., 2., 3.};
        T t{MyStruct{}, 2};

        std::get<0>(t).init(v, 1);
        }






        share|improve this answer














        share|improve this answer



        share|improve this answer








        edited Nov 19 at 15:45

























        answered Nov 19 at 15:34









        Peter Ruderman

        10.1k2352




        10.1k2352






























            draft saved

            draft discarded




















































            Thanks for contributing an answer to Stack Overflow!


            • Please be sure to answer the question. Provide details and share your research!

            But avoid



            • Asking for help, clarification, or responding to other answers.

            • Making statements based on opinion; back them up with references or personal experience.


            To learn more, see our tips on writing great answers.





            Some of your past answers have not been well-received, and you're in danger of being blocked from answering.


            Please pay close attention to the following guidance:


            • Please be sure to answer the question. Provide details and share your research!

            But avoid



            • Asking for help, clarification, or responding to other answers.

            • Making statements based on opinion; back them up with references or personal experience.


            To learn more, see our tips on writing great answers.




            draft saved


            draft discarded














            StackExchange.ready(
            function () {
            StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53374906%2fstdtuple-with-non-moveable-copyable-type%23new-answer', 'question_page');
            }
            );

            Post as a guest















            Required, but never shown





















































            Required, but never shown














            Required, but never shown












            Required, but never shown







            Required, but never shown

































            Required, but never shown














            Required, but never shown












            Required, but never shown







            Required, but never shown







            Popular posts from this blog

            Costa Masnaga

            Fotorealismo

            Sidney Franklin