C++ явный конструктор, не блокирующий преобразование double в int

У меня есть конструктор класса C из int и один из double.

Я позволяю первому делать неявное преобразование типов, но блокирую второе, используя ключевое слово явное.

но, к сожалению, возникает неявное преобразование double to int. Могу ли я как-то его заблокировать?

Вот упрощенный пример

//g++  5.4.0
#include <iostream>
using namespace std;

class C{
    int* tab;
    public:
    C():tab(nullptr){ cout<<"(void)create zilch\n"; }
    C(int size):tab(new int[size]){ cout<<"(int)create " << size << "\n"; }
    explicit C(double size):tab(new int[(int)size]){ cout<<"(double)create " << size << "\n"; }
    ~C(){ if(tab) {cout<<"destroy\n"; delete[] tab;} else cout <<"destroy zilch\n"; }
};    

int main()
{
    cout << "start\n";
    {
        C o1(1);
        C o2 = 2; //ok, implicit conversion allowed
        C o3(3.0);
        C o4 = 4.0; //ko, implicit conversion to double blocked... but goes to int 
    }
    cout << "stop\n";
}

//trace 
//
//start
//(int)create 1
//(int)create 2
//(double)create 3
//(int)create 4
//destroy
//destroy
//destroy
//destroy
//stop

person chetzacoalt    schedule 12.02.2020    source источник
comment
Не связано, но важно: никогда не используйте new и delete в C++. Это низкоуровневые операции. В вашем примере используйте std::vector<int>.   -  person Mikhail    schedule 12.02.2020
comment
Вы получаете хотя бы предупреждение компилятора?   -  person Nick    schedule 12.02.2020
comment
@Mikhail C++ может быть языком низкого уровня. Лучше сказать, что лучше их не использовать. Новые и удаляемые по-прежнему имеют свое место.   -  person sweenish    schedule 12.02.2020
comment
обман заключается в том, как предотвратить преобразование таким образом, чтобы это сработало. Если же ваш вопрос на самом деле о том, почему ваш способ не работает, просто скажите. Вопрос можно было бы возобновить   -  person 463035818_is_not_a_number    schedule 12.02.2020
comment
@Mikhail Я согласен, что никогда не бывает ненужным преувеличением. Даже с помощью интеллектуальных указателей вы должны время от времени писать new, поэтому утверждение «никогда» вводит в заблуждение.   -  person 463035818_is_not_a_number    schedule 12.02.2020
comment
@Ник, к сожалению, никаких предупреждений.   -  person chetzacoalt    schedule 12.02.2020
comment
Использование C(double size), а затем обращение к size как к int — плохой дизайн. Я бы посоветовал явно объявить C(double size) и C& operator=(double) членами private и не реализовывать их.   -  person R Sahu    schedule 12.02.2020
comment
@idclev 463035818, извините... но как мне использовать ваш обман? Я понимаю, что решение состояло в том, чтобы перегрузить оператор *, так какое же решение здесь?   -  person chetzacoalt    schedule 12.02.2020
comment
хм, я пропустил одно различие в вопросах. Здесь кажется, что вы хотите сохранить явный конструктор, который принимает двойное число (это правильно?), но предотвратить преобразование для другого конструктора   -  person 463035818_is_not_a_number    schedule 12.02.2020
comment
другими словами: вы добавили явный конструктор только для того, чтобы другой не принял double, или вы действительно хотите сохранить оба конструктора?   -  person 463035818_is_not_a_number    schedule 12.02.2020
comment
да idclev, это моя точка зрения: я хочу 2 конструктора, один явный, а другой нет. и неявное преобразование double в int мешает моему явному ключевому слову. кстати, я попытался добавить C& operator=(double i){cout ‹‹ catch ‹‹ endl; } но здесь это не поможет   -  person chetzacoalt    schedule 12.02.2020
comment
нормально открылся. но в таком случае следует немного уточнить вопрос. В вашем примере оба конструктора делают одно и то же (кроме cout, который, я полагаю, предназначен только для демонстрации), поэтому неясно, зачем вам нужны оба или почему имеет значение, какой из них вызывается   -  person 463035818_is_not_a_number    schedule 12.02.2020
comment
да извините, я постарался привести пример к минимальному коду. все комментарии о векторе и бесполезности этого кода верны, но я хотел сократить и сосредоточиться только на механизме работы. Кроме того, я полностью открыт для более значимого названия. Если у вас есть какие-либо идеи, пожалуйста, измените их :)   -  person chetzacoalt    schedule 12.02.2020
comment
template<typename T> C(T) = delete; для подавления других неявных преобразований.   -  person Eljay    schedule 12.02.2020
comment
@Элджей, спасибо! работает отлично. (как пометить ваш ответ как ответ?)   -  person chetzacoalt    schedule 12.02.2020
comment
Мой комментарий был просто комментарием и был лишь обоснованным предположением о том, что вы искали. Тем не менее, у него есть некоторые предостережения, потому что это большой молоток, который может вызвать проблемы (если только вы не педантичны в своих типах). У меня нет времени, чтобы дать правильный ответ прямо сейчас, который вы могли бы принять. Кто-то, вероятно, даст исчерпывающий и пояснительный ответ в ближайшее время.   -  person Eljay    schedule 12.02.2020
comment
защитите C(int) ctor с разрешающей чертой типа, например. template<typename I, typename = typename std::enable_if<std::is_integral<I>::value>::type> C(I size): tab(new int[size]) {...}. Вам также потребуется #include <type_traits>.   -  person arayq2    schedule 12.02.2020


Ответы (2)


Хорошо! Элджей получил это быстрее в комментариях, но вот окончательный код, который приведет к ошибке компиляции при попытке неявно использовать двойное число

#include <iostream>
using namespace std;

class C{
    int* tab;

public:
    //THE TRICK: block any implicit conversion by default
    template <class T> C(T) = delete;

    C():tab(nullptr){ cout<<"(void)create zilch\n"; }
    C(int size):tab(new int[size]){ cout<<"(int)create " << size << "\n"; }
    explicit C(double size):tab(new int[(int)size]){ cout<<"(double)create " << size << "\n"; }
    ~C(){ if(tab) {cout<<"destroy\n"; delete[] tab;} else cout <<"destroy zilch\n"; }
};

int main()
{
    cout << "start\n";
    {
        C o1(1);
        C o2 = 2; //ok, implicit conversion allowed
        C o3(3.0);
        C o4 = 4.0; //ko, implicit conversion to other types deleted
        C o5 = (C)5.0; //ok. explicit conversion
    }
    cout << "stop\n";
}
person Sérgio Castelani    schedule 12.02.2020
comment
Обратите внимание, что это также блокирует конструкторы копирования и перемещения, поэтому обязательно сделайте это вручную :( - person Mooing Duck; 12.02.2020
comment
@Mooing Duck На самом деле нет. Эти конструкторы можно использовать без дополнительной работы. - person Sérgio Castelani; 12.02.2020

Вы можете попробовать заменить конструктор C(int) на тот, который использует разрешающую черту типа, например.

#include <iostream>
#include <type_traits>
using namespace std;

class C{
    int* tab;
    public:
    C():tab(nullptr){ cout<<"(void)create zilch\n"; }
    template<typename I, typename = typename enable_if<is_integral<I>::value>::type>
    C(I size):tab(new int[size]){ cout<<"(int)create " << size << "\n"; }
    explicit C(double size):tab(new int[(int)size]){ cout<<"(double)create " << size << "\n"; }
    ~C(){ if(tab) {cout<<"destroy\n"; delete[] tab;} else cout <<"destroy zilch\n"; }
};

int main()
{
    cout << "start\n";
    {
        C o1(1);
        C o2 = 2; //ok, implicit conversion allowed
        C o3(3.0);
        C o4 = 4.0; //ko, implicit conversion to double blocked... but goes to int
    }
    cout << "stop\n";
}

Это даст вам такую ​​​​ошибку:

test.cpp: In function ‘int main()’:
test.cpp:22:16: error: conversion from ‘double’ to non-scalar type ‘C’ requested
         C o4 = 4.0; //ko, implicit conversion to double blocked... but goes to int
                ^
person arayq2    schedule 12.02.2020
comment
Элегантный подход. Просто имейте в виду, что он также примет символ: C c = 'c'. Вы можете изменить is_integral<I> на is_same<I, int> . - person Sérgio Castelani; 12.02.2020