11

There is a situation where I want to collect all the nodes names of a path to one key in JSON. Consider the condition: JSON array index "0", "1" are also allowed, but it is easy to forget the quotes, which would lead to a crash when doing dereference. So I want compiler to reject this kind of parameters. Example:

#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;
}

Refering to How do I avoid implicit conversions on non-constructing functions? I tried something as following:

#include <vector>
#include <iostream>

// I want to describe only char pointer parameter is allowed as element,
// parameter of any integer types should be rejected.
int func(const std::vector<const char*>& pin) {
    return pin.size();
}

int func(const std::vector<int>& pin) = delete;
// or
template<typename T>
int func(const std::vector<T>& pin) = delete;

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

But the compiler still can not understand me.
Any suggestion?

Please point out any misuse of terminologies and assumptions, thank you!

rustyhu
  • 1,912
  • 19
  • 28
  • 1
    is there a reason why you use `std::vector` instead of `std::vector>`? – bolov Apr 16 '20 at 05:30
  • Do you want to forbid `nullptr` also? – Jarod42 Apr 16 '20 at 06:10
  • @bolov At beginning I consider to pass these nodes names to a JSON analysis interface, which uses C-style char* as input, but this is not limited here. I have tested, using std::vector> still accepts 0 when compile, but crashes when run, on my machine GCC reports "basic_string::_M_construct null not valid". – rustyhu Apr 16 '20 at 08:42
  • @Jarod42 Yes, what want is C-style string literal. – rustyhu Apr 16 '20 at 09:54

3 Answers3

10

Something like this? It's very similar to the overload solution you suggested, but requires wrapping the vector type. Fails to build if you provide a literal 0 because the deleted constructor overload is chosen.

#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;
}
Mikel Rychliski
  • 3,455
  • 5
  • 22
  • 29
4

In hindsight many of the implicit conversions in C++ are unfortunate, this being one of them.

One option to consider is -Wzero-as-null-pointer-constant on gcc and clang. Be careful as this changes the behavior of standard programs and if enabled globally can have some unintended effects.

g++ - how do I disable implicit conversion from 0 to pointer types?

Which Clang warning is equivalent to Wzero-as-null-pointer-constant from GCC?

bolov
  • 72,283
  • 15
  • 145
  • 224
3

I like Mikel Rychliski's answer. However there already exists a solution in Guideline Support Library:

gsl::not_null

I highly recommend GSL. It's created and backed by many C++ experts, Bjarne Stroustrup himself and Herb Sutter among them. And the C++ Core Guidelines are actively being integrated into the compiler warnings and static analyzers.

bolov
  • 72,283
  • 15
  • 145
  • 224