Const Error

This is a discussion on Const Error within the C++ Programming forums, part of the General Programming Boards category; Hi, I was just testing and trying to figure out why this results in an error. I assume it has ...

  1. #1
    Dae
    Dae is offline
    Deprecated Dae's Avatar
    Join Date
    Oct 2004
    Location
    Canada
    Posts
    1,034

    Const Error

    Hi, I was just testing and trying to figure out why this results in an error. I assume it has to do with how the compiler is storing the lambda argument 'self' but I can't figure out why it would make it const. I think lambda's are pretty much just turned into local structs with arguments as member variables. I want it to make a copy, using the copy constructor. I have a reason for that, this is trimmed. (Using 'this' can currently result in dangling pointers). Anything I could try?

    Edit: it just hit me to check the GCC C++0x page, and this const feature isn't actually implemented yet... so it won't work. Thoughts and opinions are still welcome though (use of reference, etc.)

    Code:
    class impl
    {
    public: 
        impl() { }
        impl(impl& p) { }
    
        void connecter()
        {
            impl self(*this); // trying to copy current object
    
            boost::function<void()> on_connect = [self]() // trying to pass by value
            {
                self.something(); // error
    
    // edit: this entire scope is const apparently
            };
        }
    
        void something()
        {
    
        }
    };
    error: passing ‘const impl’ as ‘this’ argument of ‘void impl::something()’ discards qualifiers|
    It works if I use a reference (ie. &self) but after the function scope ends, wouldn't 'self' be deleted, or something? Isn't that unsafe? Undefined behavior? Dangling reference?

    It also works if I make something() const, but then I'd never be able to modify anything because I haven't solved the problem of why it's making it const to begin with.

    Thanks in advance
    Last edited by Dae; 09-21-2009 at 05:38 AM. Reason: changed the function name from print() to something()
    Warning: Have doubt in anything I post.

    GCC 4.5, Boost 1.40, Code::Blocks 8.02, Ubuntu 9.10 010001000110000101100101

  2. #2
    Guest Sebastiani's Avatar
    Join Date
    Aug 2001
    Location
    Waterloo, Texas
    Posts
    5,659
    I'm not sure why it does that, but then again I know very little about C++0x.

    But on a side note, the impl copy constructor should take a const reference to an impl. Or maybe that was just a typo (come to think of it, I can't see how it would have compiled, actually)?

    As to the scoping issue, if the lambda is being invoked some time after 'connector' has already completed, then yes, you'll have a dangling reference.

    If nothing else, I guess you could use const_cast, but that doen't seem like a very good solution, in the long run.

  3. #3
    Dae
    Dae is offline
    Deprecated Dae's Avatar
    Join Date
    Oct 2004
    Location
    Canada
    Posts
    1,034
    Quote Originally Posted by Sebastiani View Post
    I'm not sure why it does that, but then again I know very little about C++0x.

    But on a side note, the impl copy constructor should take a const reference to an impl. Or maybe that was just a typo (come to think of it, I can't see how it would have compiled, actually)?

    As to the scoping issue, if the lambda is being invoked some time after 'connector' has already completed, then yes, you'll have a dangling reference.

    If nothing else, I guess you could use const_cast, but that doen't seem like a very good solution, in the long run.
    According to wikipedia that copy constructor is valid. It's not a typo though, when you use 'this' as the argument, it's not const (I read, and verified). It compiles. Before I posted the topic I tried const_cast but it said 'this' (tried variations) wasn't a valid pointer, reference, etc.

    It's actually just a problem with lambda apparently as per my edit. It'll always be const (until they approve and implement that feature).

    Thanks, I wasn't sure about that reference. I might just have to put up with const for now (it doesn't cause any issues with variables wrapped in smart pointers).

    Any other thoughts?
    Last edited by Dae; 09-21-2009 at 04:29 AM.
    Warning: Have doubt in anything I post.

    GCC 4.5, Boost 1.40, Code::Blocks 8.02, Ubuntu 9.10 010001000110000101100101

  4. #4
    The larch
    Join Date
    May 2006
    Posts
    3,573
    A member function called print should be marked const with a very high likelihood (principle of least surprise). Also the copy constructor should take a const reference, unless you have a very good reason (only auto_ptr takes a non-const reference and everybody hate it).

    Unfortunately I don't have a compiler that supports lambda.
    I might be wrong.

    Thank you, anon. You sure know how to recognize different types of trees from quite a long way away.
    Quoted more than 1000 times (I hope).

  5. #5
    Guest Sebastiani's Avatar
    Join Date
    Aug 2001
    Location
    Waterloo, Texas
    Posts
    5,659
    Quote Originally Posted by Dae View Post
    According to wikipedia that copy constructor is valid. It's not a typo though, when you use 'this' as the argument, it's not const (I read, and verified). It compiles. Before I posted the topic I tried const_cast but it said 'this' (tried variations) wasn't a valid pointer, reference, etc.

    It's actually just a problem with lambda apparently as per my edit. It'll always be const (until they approve and implement that feature).

    Thanks, I wasn't sure about that reference. I might just have to put up with const for now (it doesn't cause any issues with variables wrapped in smart pointers).

    Any other thoughts?
    Interesting. I didn't know that.

  6. #6
    Dae
    Dae is offline
    Deprecated Dae's Avatar
    Join Date
    Oct 2004
    Location
    Canada
    Posts
    1,034
    Quote Originally Posted by anon View Post
    A member function called print should be marked const with a very high likelihood (principle of least surprise). Also the copy constructor should take a const reference, unless you have a very good reason (only auto_ptr takes a non-const reference and everybody hate it).

    Unfortunately I don't have a compiler that supports lambda.
    Just remove the lambda. The important part is if you remove that constructor my compiler throws a "no matching function" call to impl& if you use *this even if I have the const copy constructor. I don't see why it matters, it's still a constructor and doesn't do anything else. They hate auto_ptr itself, or they hate that it takes a reference instead of a const reference?

    Yeah, print should be const but it's just an example brah Call it whatever you want.
    Warning: Have doubt in anything I post.

    GCC 4.5, Boost 1.40, Code::Blocks 8.02, Ubuntu 9.10 010001000110000101100101

  7. #7
    Cat without Hat CornedBee's Avatar
    Join Date
    Apr 2003
    Posts
    8,893
    If you want to modify a value capture, you can do it like this:
    Code:
    auto lambda = [value] mutable { value.modify(); };
    But I think you mentioned that GCC doesn't support that yet.

    I'm not sure what the current issue is. Are you saying that this doesn't compile:
    Code:
    struct impl
    {
      void foo()
      {
        impl local(*this);
      }
    };
    ?
    All the buzzt!
    CornedBee

    "There is not now, nor has there ever been, nor will there ever be, any programming language in which it is the least bit difficult to write bad code."
    - Flon's Law

  8. #8
    Dae
    Dae is offline
    Deprecated Dae's Avatar
    Join Date
    Oct 2004
    Location
    Canada
    Posts
    1,034
    Wow, I was very very wrong. I should have done an isolated check. Ignore what I said about const and copy constructors.

    What happened was my other constructor was interfering. Variadic template constructors seem to have a higher precedence than copy constructors. I think that's kind of stupid, and I'm not sure how to solve that problem other than not using them (I tried defaulting copy constructors even though its a hassle, and explicit variadic constructor). I can only specialize the variadic template if I specialize the class (if it's a template) which is less than ideal.

    Code:
    #include <iostream>
    
    struct impl
    {
      impl(impl const& p) { std::cout << "yay" << std::endl; } // this is not called
    
      template<typename ...argument_types>
      impl(argument_types&& ...argument_list) { std::cout << "boo" << std::endl; } // this is called
    };
    
    int main()
    {
        impl a;
        impl b(a); // boo
    }
    Remove const and it works:

    Code:
    #include <iostream>
    
    struct impl
    {
      impl(impl& p) { std::cout << "yay" << std::endl; } // this is called
    
      template<typename ...argument_types>
      impl(argument_types&& ...argument_list) { std::cout << "boo" << std::endl; }
    };
    
    int main()
    {
        impl a;
        impl b(a); // yay
    }
    That's why I wasn't using const.
    Last edited by Dae; 09-21-2009 at 04:07 PM.
    Warning: Have doubt in anything I post.

    GCC 4.5, Boost 1.40, Code::Blocks 8.02, Ubuntu 9.10 010001000110000101100101

  9. #9
    Cat without Hat CornedBee's Avatar
    Join Date
    Apr 2003
    Posts
    8,893
    The variadic constructor is instantiated as "impl(impl& &&)" which collapses to "impl(impl&)". This is a better match for the non-const lvalue argument "a" than the const copy constructor.

    This is an interesting issue. I would suggest SFINAEing out the offending instantiation of the variadic constructor, but that actually seems not possible without seriously inconveniencing the client of the class. So you might have to write both const and non-const variants of the copy constructor; the problem is that AFAIK GCC doesn't support constructor forwarding yet, so you actually have to duplicate the implementation (at least the init lists, the other stuff can be combined).

    Alternatively, make it so your variadic constructor does the right thing for a single argument of its class type. But that may not be a design option.

    Hmm ... you might want to write to comp.std.c++ about this problem.
    All the buzzt!
    CornedBee

    "There is not now, nor has there ever been, nor will there ever be, any programming language in which it is the least bit difficult to write bad code."
    - Flon's Law

  10. #10
    Dae
    Dae is offline
    Deprecated Dae's Avatar
    Join Date
    Oct 2004
    Location
    Canada
    Posts
    1,034
    I actually had PIMPL in mind, and variadic constructors seemed like a nice solution until they killed the copy constructors and various other features depending on how they're used (initializer lists).

    Quote Originally Posted by CornedBee View Post
    I would suggest SFINAEing out the offending instantiation of the variadic constructor, but that actually seems not possible without seriously inconveniencing the client of the class.
    Yeah, and the entire purpose of the variadic constructor is convenience of the class clients unfortunately.

    Quote Originally Posted by CornedBee View Post
    the problem is that AFAIK GCC doesn't support constructor forwarding yet, so you actually have to duplicate the implementation (at least the init lists, the other stuff can be combined).
    That looks like the case.

    Quote Originally Posted by CornedBee View Post
    Alternatively, make it so your variadic constructor does the right thing for a single argument of its class type. But that may not be a design option.
    Yup, not possible in this case unfortunately :[

    Quote Originally Posted by CornedBee View Post
    Hmm ... you might want to write to comp.std.c++ about this problem.
    I'm too noob, I'd get eaten alive.

    Everything works non-const and explicit variadic constructor except certain things like initializer lists.

    Thanks much CornedBee, appreciate the responses :]

    It's is probably too much work.. but for instance this is kind of what I was thinking. What do you think?

    main.h
    Code:
    #ifndef MAIN_H
    #define MAIN_H
    
    #include <iostream>
    #include <boost/shared_ptr.hpp>
    
    class klass
    {
        class impl;
    
    public: 
        klass();
        klass(klass& p);
        klass(klass const& p);
        template<typename ...argument_types> explicit klass(argument_types&& ...argument_list);
    
        void do_something();
        void print_multiple();
    
    private: 
        boost::shared_ptr<impl> operator->();
    
        boost::shared_ptr<impl> data;
    };
    
    #endif
    main.cpp
    Code:
    #include "main.h"
    
    class klass::impl
    {
    public:
        impl() { }
    
        impl(int x, int y) : x(x), y(y) { }
    
    private:
        int x;
        int y;
    
        friend class klass;
    };
    
    klass::klass() : data(new klass::impl) { }
    
    template<typename ...argument_types>
    klass::klass(argument_types&& ...argument_list) : data(new klass::impl(argument_list...)) { }
    
    klass::klass(klass& p) : data(p.data) { }
    
    klass::klass(klass const& p) : data(p.data) { }
    
    boost::shared_ptr<klass::impl> klass::operator->() { return this->data; }
    
    void klass::do_something()
    {
        auto self = *this;
    
        self->x = 10;
        self->y = 10;
    }
    
    void klass::print_multiple()
    {
        auto self = *this;
    
        std::cout << self->x * self->y << std::endl;
    }
    
    int main()
    {
        klass a(5, 5); // klass a = {5, 5}; doesn't work
    
        a.print_multiple(); // 25
    
        a.do_something(); // change to 10 x 10
    
        a.print_multiple(); // 100
    
        klass b(a);
    
        b.print_multiple(); // 100
    }
    Last edited by Dae; 09-22-2009 at 06:49 AM.
    Warning: Have doubt in anything I post.

    GCC 4.5, Boost 1.40, Code::Blocks 8.02, Ubuntu 9.10 010001000110000101100101

  11. #11
    Cat without Hat CornedBee's Avatar
    Join Date
    Apr 2003
    Posts
    8,893
    Bad idea. PIMPL doesn't play nice with template member functions, because the implementation of those must be visible to all clients of the class, when the entire point of PIMPL is to hide the implementation from the clients. Those goals are incompatible.
    All the buzzt!
    CornedBee

    "There is not now, nor has there ever been, nor will there ever be, any programming language in which it is the least bit difficult to write bad code."
    - Flon's Law

  12. #12
    Dae
    Dae is offline
    Deprecated Dae's Avatar
    Join Date
    Oct 2004
    Location
    Canada
    Posts
    1,034
    Oh dude, I knew that too, and then I forgot. I guess I was hoping C++0x solved that problem, even though I don't think it will ever be possible (the way C++ templates work).

    That example was working pretty cool too. Oh well. I'm going to ponder this further.
    Warning: Have doubt in anything I post.

    GCC 4.5, Boost 1.40, Code::Blocks 8.02, Ubuntu 9.10 010001000110000101100101

  13. #13
    Dae
    Dae is offline
    Deprecated Dae's Avatar
    Join Date
    Oct 2004
    Location
    Canada
    Posts
    1,034
    I actually ended up going with your SNIFAE suggestion. Without SNIFAE it was working good until I tried to assign an object via a hash map, it was then calling the variadic constructor instead of the copy constructor.

    Code:
    this_type(this_type& t) : data(t.data) { }
    
    this_type(this_type const& t) : data(t.data) { }
    
    template<typename ...argument_types> explicit this_type(argument_types&& ...argument_list) : data(new data_type(argument_list...)) { }
    
    template<typename T> this_type(T&& t, typename boost::enable_if<boost::is_base_of<this_type, T>>::type* dummy = 0) : data(t.data) { }
    I threw all of it into a macro to ease the inconvenience. It only works out fine because of a weird design I'm using for my project.

    Thanks again CornedBee.
    Last edited by Dae; 09-25-2009 at 03:40 AM.
    Warning: Have doubt in anything I post.

    GCC 4.5, Boost 1.40, Code::Blocks 8.02, Ubuntu 9.10 010001000110000101100101

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Quantum Random Bit Generator
    By shawnt in forum C++ Programming
    Replies: 62
    Last Post: 06-18-2008, 10:17 AM
  2. Avoiding Global variables
    By csonx_p in forum Windows Programming
    Replies: 32
    Last Post: 05-19-2008, 12:17 AM
  3. Game Pointer Trouble?
    By Drahcir in forum C Programming
    Replies: 8
    Last Post: 02-04-2006, 01:53 AM
  4. Half-life SDK, where are the constants?
    By bennyandthejets in forum Game Programming
    Replies: 29
    Last Post: 08-25-2003, 11:58 AM
  5. Linking error
    By DockyD in forum C++ Programming
    Replies: 10
    Last Post: 01-20-2003, 04:27 AM

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21