2017-01-18 8 views
1

У меня есть класс с конструктором, который принимает неконстантную ссылку, которая назначается члену класса. Теперь я хочу создать объект const указанного класса, но конструктор жалуется, если я передаю ссылку const на конструктор. Код ниже, который упрощает мой оригинальный код, демонстрирует проблему.Построение объекта const с использованием неконстантных

Насколько я могу судить, не должно возникнуть проблем с созданием объекта const, поскольку оно основано на данных const?

Как достичь того, что я пытаюсь сделать в create_const_a?

#include <iostream> 

class A { 
private: 
    double &d; 
    const int &i; 
public: 
    A(double &dd, const int &ii) 
    : d(dd), i(ii) 
    {} 

    void set_d(double a) { 
    d = a; 
    } 

    void print() const { 
    std::cout << d << " " << i << std::endl; 
    } 
}; 


A create_a(double &dd, const int &ii) { 
    return A(dd,ii); 
} 

const A create_const_a(const double &dd, const int &ii) { 
    return A(dd,ii); 
} 


void foo(A a) 
{ 
    a.set_d(1.3); 
    a.print(); 
} 

void bar(const A a) 
{ 
    a.print(); 
} 


int main(int argc, char *argv[]) 
{ 
    double d = 5.1; 
    int i = 13; 

    foo(create_a(d,i)); 

    bar(create_const_a(d,i)); 

    return 0; 
} 

Ошибки я получаю:

test.cc: In function ‘const A create_const_a(const double&, const int&)’: 
test.cc:27:17: error: binding ‘const double’ to reference of type ‘double&’ discards qualifiers 
    return A(dd,ii); 
       ^
test.cc:8:3: note: initializing argument 1 of ‘A::A(double&, const int&)’ 
    A(double &dd, const int &ii) 
^

Update: После обучения некоторых новых вещей о том, как сопзЬ работает с объектами и неконстантными ссылками в них, я в конце концов решил исходную задачу путем введения другого типа, например, ConstA, который содержит только ссылки на const, которые затем могут использоваться в каждом из проблемных случаев.

+1

Вы можете перейти от неконстантного к const, но не наоборот. Конструктор нуждается в не-const, и ваш 'create_const_a' получает const. – AndyG

+5

Все эти ссылки ... Если вы продолжите, вы рано или поздно придете к указателю, где у вас есть болтливая ссылка, и плохие вещи произойдут. * Почему у вас есть ссылки в классе 'A'? Что такое * актуальная проблема, которую вы пытаетесь решить с помощью такого решения? –

+1

Ваш метод 'create_const_a' возвращает ссылку на локальный объект. Это не работает так, как вы думаете. См. [Этот ответ] (http://stackoverflow.com/a/4643721/7359094) для получения дополнительной информации. –

ответ

1

C++ запрещает это, чтобы избежать преобразования ссылки const на не-const.

Вот небольшой пример того, как это будет происходить:

struct foo { 
    int& a; 
    foo(int& b) : a(b) {} 
    void bar() const { 
     a = 5; 
    } 
}; 

выше компилирует хорошо, потому что a = 5 делает не изменение состояния foo объекта; он изменяет состояние внешнего int, поэтому foo::bar разрешено быть const.

Теперь предположим, что мы могли бы сделать это:

const foo make_const(const int& x) { 
    return foo(x); // Not allowed 
} 

Тогда мы могли бы написать

const foo f(make_const(10)); 
f.bar(); 

и изменить ссылку на временную int, которое неопределенное поведение.

Вот небольшой demo:

int x = 10; 
cout << x << endl; 
const foo f(x); 
f.bar(); 
cout << x << endl; 

Он печатает

10 
5 
+0

Тот факт, что ссылка может быть изменена без нарушения const-correctness, была совершенно новой для меня и объясняет многое, почему это кажется невозможным. – kalj

+0

@kalj Это самая сложная часть 'const'-ness, когда она применяется к ссылкам и указателям. Указатель или ссылка могут быть 'const', в то время как элемент, к которому они указывают/ссылку, останется доступным для записи. – dasblinkenlight

0

Вы пытаетесь бросить const Нессов:

const A create_const_a(const double &dd, const int &ii) { 
    return A(dd,ii); 
} 

но dd является неконстантным, так созданный объект A не знал бы, что i t не может изменить его член d, в то время как вызывающий абонент create_const_a передаст переменную, полагая, что так.

Вы не можете ходить с вне делать копию dd или опуская const (например, через const_cast), который является опасным и настоятельно указывает на ошибку в дизайне.

Ваше определение A говорит: «Мне нужно иметь возможность изменять d», но в create_const вы обещаете, что он не будет изменен.

Другими словами, вы должны устранить ошибку в дизайне. Либо освободите ограничение, либо сделайте копию. Устранение «ошибки уйти» просто затмит симптом плохого дизайна, пока вы не столкнетесь с реальными проблемами, когда нарушение контракта окажется в последствии.

0

Функция const A create_const_a принимает в качестве первого аргумента ссылку на константу double, а затем пытается использовать ее в ctor как non const ref, которая недопустима.

Вы должны удалить квалификатор const.

Если вы действительно уверены, почему вы можете сделать это здесь, это означает, что если вы можете быть уверены, что create_const будет ВСЕГДА получать письменные константные параметры, вы можете явно использовать const_cast для удаления спецификатора:

const A create_const_a(const double &dd, const int &ii) { 
    return A(const_cast<double &>(dd),ii); 
} 

Но BEWARE, это приведет к Undefined Behavior, если вы сделаете это на переменной, которая изначально была объявлена ​​как const.