简体   繁体   中英

std::string used with template, check if empty

I would like to use a function that takes in a general parameter of type T , and within the function check if the variable passed in is NULL . I could simply do this with primitive types like this:

if (var) doSomething();

However, in order to check if it's an std::string , I would need to do:

if (!var.empty()) doSomething();

What would be the best, most general way to check if the parameter being passed in is NULL ? I would like to use all primitive types and std::string . Perhaps I could use typeid ?

First of all, you'd have better luck researching this topic if you used proper terminology. An empty string is not " NULL "; a zero-equivalent integer is also not " NULL ". The only thing that can be " NULL " is a pointer (though we use nullptr for those nowadays). So right away we can stop using that term.

There is no general "not set" value for arbitrary types, and therefore no common way to detect such a case. However, you can use a wrapper type that explicitly adds this possibility, such as boost::optional<T> :

#include <boost/optional.hpp>
#include <iostream>

template <typename T>
void foo(boost::optional<T> arg)
{
   if (arg)
      std::cout << arg.get() << '\n';
}

int main()
{
    boost::optional<int>   a{123};
    boost::optional<int>   b{boost::none};
    boost::optional<float> c{boost::none};
    boost::optional<float> d{123.456};

    foo(a);
    foo(b);
    foo(c);
    foo(d);

    // Or, pass it directly:
    foo(boost::optional<std::string>{"abc"});
    foo(boost::optional<std::string>{boost::none});
    foo(boost::optional<std::string>{});  // as if boost::none were given
}

// $ g++ -std=c++14 -O2 -Wall -Wextra -pedantic -pthread main.cpp && ./a.out
// 123
// 123.456
// abc

( live demo )

Your only other option would be to accept pointers-to-objects and check for NULL ity on the pointer. But that is an abstraction leak, because accepting a pointer indicates an intent other than that of this function; it also creates a bit of a mess with respect to clear ownership semantics.

You could have generic code without repeating yourself by using a template that compares to a default constructed version of the object:

#include <iostream>
#include <string>

template <typename T>
void doSomething(T t)
{
    std::cout << "Doing something with " << t << " \n";
}

void doSomethingElse()
{
    std::cout << "Doing something else\n";
}

template <typename T>
void f(T t)
{
    if (t!=T{}) doSomething(t); // compare with default constructed object
    else doSomethingElse();
}

int main()
{
    int var1{};
    std::string var2{};

    int var3{3};
    std::string var4{"var4's text"};

    f(var1);
    f(var2);
    f(var3);
    f(var4);
}

Produces:

Doing something else
Doing something else
Doing something with 3
Doing something with var4's text

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM