【问题标题】:C++ - return different variable typesC++ - 返回不同的变量类型
【发布时间】:2013-09-28 10:39:13
【问题描述】:

我不知道这是否可能,但也许还有其他解决方案可以满足我的需求。我正在尝试从设置文件中获取设置。它们可以是字符串(如名称)、整数或布尔值。当然,它们以文本形式存储在文件中,但我将创建一个用于打开和返回设置的类,但不是作为字符串,而是作为它们中的每一个实际上是什么。

class Settings {
    public:
        Settings(string FileName);
        template <class T> T Setting(string SettingName);
}

例如,构造函数会加载文件、解析设置并将它们存储为地图。 现在,当我调用 Setting 成员函数时,我希望它识别所请求设置的值是什么类型(如果是数字,整数,如果是“真”或“假”布尔值,如果是字母数字字符串)并返回该类型的值。一个例子

Settings UserPreferences("Preferences.cfg");
bool AutoLogin = UserPreferences.Setting("autologin");  // return bool
string UserName = UserPreferences.Setting("username"); // return string or char*

我查看了模板,但看起来我必须在创建 Settings 对象时指定我期望的变量,但这不是重点。我很高兴像这样声明要返回的变量类型:

bool AutoLogin = UserPreferences.Setting<bool>("autologin");
string UserName = UserPreferences.Setting<string>("username");

但我不知道这是否可能。你怎么看?

【问题讨论】:

  • 我有这样一个运行良好的类。所以是的,我认为这是可能的。 ;)
  • 我很惊讶它的工作,因为你不能重载返回类型。
  • @BoBTFish:但它们不是重载。他们是专业。模板参数不能从返回类型推导出来,但是显式给出就可以了。
  • @BoBTFish 另一个类似的例子是 std::make_shared 不带参数调用:每个调用与其他调用的不同之处仅在于其返回类型。
  • @BoBTFish:顺便说一句,实际上有一种情况可以重载返回类型。它会在这里允许一些很好的语法糖,尽管它可能会让初学者感到困惑。

标签: c++ templates variables types return


【解决方案1】:

这绝对是可能的,尽管你必须保证它可以转换为给定的类型。这在 XNA 的 ContentLoader 中很常见(尽管是一个非常不同的系统)。您可以使用这种方法来简化和抽象事物的存储和检索方式。考虑:

class Loader
{
private:
    vector<void*> _items;
public:
    template <typename Type>
    Type GetItem( int index ) { return (Type)(_items[ index ]); }
};

这个想法是,只要您可以可靠地将内部数据转换为请求的类型(比示例更可靠),它就是一个完全合法的操作。如何保证成功完全是另一个问题,但您绝对可以拥有返回类型与其模板类型相同的方法。考虑以下示例(我使用这是一个用于资源加载器的大学项目):

头文件.h

class BasicResource
{
public:
    static const int ResourceID;
    const int ID;
    BasicResource( )
        : ID( ResourceID )
    {
    }
};

class Loader
{
private:
    vector<BasicResource*> _items;
public:
    template <typename Type>
    Type GetItem( int index );
};

#include "inline.inl"

内联.inl

template <typename Type>
Type Loader::GetItem( int index )
{
    auto item = _items[ index ];
    if( item != nullptr && item->ID == Type::ResourceID )
    {
        return (Type)_item;
    }
    else
    {
        // Handle the fail case somehow
    }
}

内联文件允许您像往常一样分离您的逻辑,但将其包含在允许导出模板方法的标题中。

【讨论】:

  • 谢谢,威廉。您的示例有效,但我希望将其包含在 DLL 中。问题是我不能(或不知道如何)在.h(接口)和.cpp(实现)之间拆分类。我什至不知道这是否可能。是吗?
  • @ali 模板代码必须在某个头文件中(通常)。因此,您只能在 .cpp 中移动非模板化实现,例如构造函数
  • 那么,我不能在 .cpp 中移动模板化函数的代码并在标题中留下声明吗?不能从共享/动态库中导出模板化成员吗?
【解决方案2】:

是的,这当然是可能的。我写了以下完整的代码来证明这一点:

#include <iostream>
#include <map>
#include <string>
#include <sstream>
#include <stdexcept>


struct Settings
{
  typedef std::map<std::string, std::string> SettingsMap;
  template <class T> T as( const std::string& name ) const
  {
    std::istringstream is( getEntry( name ) );
    T value;
    if( is )
    {
      if( (is >> value) || (is.eof() && !is.fail()) )
      {
        return value;
      }
    }
   //Exception handling not in scope of question
   throw std::runtime_error( "..." );
};

const std::string& getEntry( const std::string& name ) const
{
  SettingsMap::const_iterator pos( settingsMap_.find( name ) );
  if( pos != settingsMap_.end() )
  {
    return pos->second;
  }
  //Not part of the scope of this answer....
  throw std::invalid_argument( "No such setting..." );
}

Settings()
{
  settingsMap_["mybool"] = "1";
  settingsMap_["myint"] = "5";
  settingsMap_["myfloat"] = "43.2";
}

SettingsMap settingsMap_;
};

int main()
{
  Settings s;
  std::cout << s.as<bool>("mybool") << " "
    << s.as<int>("myint") << " "
    << s.as<float>("myfloat");

  return 0;
}

我已经实现了类似的东西,但是我使用 boost::any 作为我的映射类型,并且我在第一次解析期间读取了实际类型,因此确保存储的类型是正确的。我也使用了 boost::lexical_cast 而不是原生 istringstream,但为了证明这一点,我省略了它。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2016-06-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-05-17
    • 2018-08-30
    • 1970-01-01
    • 2012-08-06
    相关资源
    最近更新 更多