开发者

returning a reference to temporary

I understand that it's illegal to return a reference to a temporary, but here's my problem:

const stringSet & Target::dirList( const dirType type ) const
{
    switch( type )
    {
        case SOURCE_DIR:
            return m_sourceDirs;
        case HEADER_DIR:
            return m_headerDirs;
        case RESOURCE_DIR:
            return m_resourceDirs;
        default:
            return stringSet(); // PROBLEM HERE!
    }
}

The three first three options return a const reference to a stringSet data member. What should I do for the default case? If I leave it out, the compiler (GCC with -Wall -Wextra -pedantic) complains and I don't want it to because those options tend to ca开发者_如何学运维tch my bed design choices in the most odd of ways :)

Thanks!


Keep a default set as a member too... and return a reference to it. That is of course if the default case is theoretically possible. If it is not, throw an exception and don't return anything.

default:
   throw invalid_argument_exception();


const stringSet & Target::dirList( const dirType type ) const
{
    static const stringSet defaultSet; // <--
    switch( type )
    {
        case SOURCE_DIR:
            return m_sourceDirs;
        case HEADER_DIR:
            return m_headerDirs;
        case RESOURCE_DIR:
            return m_resourceDirs;
        default:
            return defaultSet; // <--
    }
}


You can't return a reference to a temporary object you created on the stack. I'll be destroyed by the time your function returns, and your application will crash.

If you intend on doing something like that, you'll have to return by value rather than by reference, i.e.

stringSet Target::dirList( const dirType type ) const

This can obviously have performance implications since you will very likely end up having the copy constructor getting called for your other references. The alternative would be to avoid creating a temporary object on the stack. Depending on your application, there are several ways you could do it, like having a simple pool that you get your temporary objects from and that are being garbage-collected at some point, or you could have dirList take a stringSet argument that is being populated by your function.

Best case - can't you just have a permanent default set somewhere? Does it have to be unique per call?


Not necessarily always the right option, but for what it's worth you can use shared_ptr for this -- construct a shared_ptr with a null deleter and return that if the string set already exists, otherwise construct one pointing to an empty set and having a normal deleter and return that. In other words:

#include <set>
#include <string>

#include <boost/shared_ptr.hpp>

struct NullDeleter
{
    void operator()(void *p) {}
};

enum DirType
{
    SOURCE_DIR,
    HEADER_DIR,
    RESOURCE_DIR,
    OTHER,
};

typedef std::set<std::string> StringSet;
typedef boost::shared_ptr<const StringSet> StringSet_CPtr;

struct Target
{
    StringSet m_sourceDirs, m_headerDirs, m_resourceDirs;

    Target()
    {
        m_sourceDirs.insert("/source");
        m_headerDirs.insert("/header");
        m_resourceDirs.insert("/resources");
    }

    StringSet_CPtr dir_list(DirType type)
    {
        switch(type)
        {
        case SOURCE_DIR:
            return StringSet_CPtr(&m_sourceDirs, NullDeleter());
        case HEADER_DIR:
            return StringSet_CPtr(&m_headerDirs, NullDeleter());
        case RESOURCE_DIR:
            return StringSet_CPtr(&m_resourceDirs, NullDeleter());
        default:
            return StringSet_CPtr(new StringSet);
        }
    }
};

int main()
{
    Target t;
    StringSet_CPtr  sourceDirs = t.dir_list(SOURCE_DIR),
                    headerDirs = t.dir_list(HEADER_DIR),
                    resourceDirs = t.dir_list(RESOURCE_DIR),
                    otherDirs = t.dir_list(OTHER);
    return 0;
}


There are directives you can put in an impossible-case default handler to say that it's unreachable. Compiler-specific and non-portable, but most compilers have something. I just don't remember how GCC spells this.

EDIT found the syntax...

switch (whatever)
{
  case blah :
    ...;
    break;
  default :
    __builtin_unreachable ();
}

As I said, this is a GCC-specific feature - there's a differently-spelled Visual C++ equivalent, though.

BTW - there's always return *((stringSet*) 0);.

EDIT Or throw an exception.

Either way (except the exception, maybe), only if you're really sure it will never happen.


If I understood you correctly..........
Here's how I do it with such switches.
Two notes regarding this code:
1. I hate using "&" ref and prefer the "* const" one (more readable) so please adjust. It's the same in essence.
2. Didn't test this code.

const stringSet * const
Target::dirList( const dirType type ) const
{
    const stringSet * pRet = NULL;


    switch( type )
    {
        case SOURCE_DIR:
            stringSet = m_sourceDirs;
        case HEADER_DIR:
            stringSet = m_headerDirs;
        case RESOURCE_DIR:
            stringSet = m_resourceDirs;
    }



    return pRet;
}
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜