Как избежать неявных преобразований из int (0) в указатель в векторе

9

Есть ситуация, когда я хочу собрать все имена узлов пути к ключу в JSON. Рассмотрим условие индекса массива «0», «1» также допускается, но легко забыть кавычки, которые могут привести к сбою при разыменовании. Поэтому я хочу отказаться от этого. Пример:

#include <vector>
#include <iostream>

int func(const std::vector<const char*>& pin) {
    return pin.size();
}

int main() {
    // {"aname", "3", "path", "0"} wanted but this still compile
    std::cout << func({"aname", "3", "path", 0}) << std::endl;
}

Я нашел и попробовал это. Как избежать неявных преобразований в не конструирующих функциях? следующим образом:

#include <vector>
#include <iostream>

int func(const std::vector<const char*>& pin) {
    return pin.size();
}

template<typename T>
int func(T pin) = delete;

int main() {
    std::cout << func({"aname", "3", "path", 0}) << std::endl;
}

Но компилятор до сих пор не понял меня.

Любое предложение?
Пожалуйста, укажите на любое неправильное использование терминологии и предположений, спасибо!

rustyhu
источник
Есть ли причина, почему вы используете std::vector<const char*>вместо std::vector<std::string>>?
Болов
Вы хотите nullptrтакже запретить ?
Jarod42
@bolov Сначала я собираюсь передать имена этих узлов в интерфейс анализа JSON, в котором в качестве входных данных используется char * в стиле C, но здесь это не ограничивается. Я проверил, используя std :: vector <std :: string >>, все еще принимает 0 при компиляции, но вылетает при запуске, на моей машине GCC сообщает "basic_string :: _ M_construct null not valid".
ржавых
@ Jarod42 Да, что нужно, так это строковый литерал в стиле C.
rustyhu

Ответы:

9

Что-то вроде этого? Это очень похоже на решение по перегрузке, которое вы предложили, но требует переноса векторного типа. Не в состоянии строить, если вы предоставляете литерал, 0потому что выбрана перегрузка удаленного конструктора.

#include <memory>
#include <new>
#include <vector>
#include <iostream>
using std::vector;

template<typename T>
struct no_zero {
        no_zero(T val) : val(val) {}
        no_zero(int val) = delete;
        operator T() { return val; }
        T val;
};

int func(const vector<no_zero<const char*> >& pin) {
    return pin.size();
}

int main() {
    // {"aname", "3", "path", "0"} wanted but this still compile
    std::cout << func({"aname", "3", "path", 0}) << std::endl;
}
Микель Рычлиски
источник
4

Оглядываясь назад, многие из неявных преобразований в C ++ неудачны, и это один из них.

Один вариант для рассмотрения -Wzero-as-null-pointer-constantна gcc и clang. Будьте осторожны, так как это меняет поведение стандартных программ и, если оно включено глобально, может привести к непредвиденным последствиям.

g ++ - как отключить неявное преобразование из 0 в типы указателей?

Какое предупреждение Clang эквивалентно константе Wzero-as-null-pointer из GCC?

Болов
источник
3

Мне нравится ответ Микеля Рыхлиски . Однако в Библиотеке поддержки уже существует решение :

gsl::not_null

Я очень рекомендую GSL. Он создан и поддерживается многими экспертами C ++, в том числе Бьярном Страуструпом и Хербом Саттером. А основные принципы C ++ активно интегрируются в предупреждения компилятора и статические анализаторы.

Болов
источник