Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/templates/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ 在一个向量中保存具有不同模板参数的类实例,但保留其属性_C++_Templates_Sfinae - Fatal编程技术网

C++ 在一个向量中保存具有不同模板参数的类实例,但保留其属性

C++ 在一个向量中保存具有不同模板参数的类实例,但保留其属性,c++,templates,sfinae,C++,Templates,Sfinae,我想有一个程序,为我分析和管理命令行参数。正如您在main-函数中所看到的,通过使用简单的命令,如Option(“number”、{“-n”、“--number”})可以指定选项值应该具有的类型(如本例中的int),每个选项都有一个唯一的标识符(如“number”),和多个字符串,此选项可以与一起引入。此外,许多选项应该封装在名为OptionSet的类中,这样可以简化对其选项的访问 但在我的实际代码中,我现在有几个问题: 我想在一个std::vector中存储具有不同模板参数的一个类的多个实例。

我想有一个程序,为我分析和管理命令行参数。正如您在
main
-函数中所看到的,通过使用简单的命令,如
Option(“number”、{“-n”、“--number”})
可以指定选项值应该具有的类型(如本例中的
int
),每个选项都有一个唯一的标识符(如
“number”
),和多个字符串,此选项可以与一起引入。此外,许多选项应该封装在名为
OptionSet
的类中,这样可以简化对其选项的访问

但在我的实际代码中,我现在有几个问题:

  • 我想在一个
    std::vector
    中存储具有不同模板参数的一个类的多个实例。例如,在我的代码中,
    选项
    应该存储在同一个向量中,就像
    选项
    选项

    甚至可以将模板参数单独存储在另一个向量中
  • 通过使用
    using
    std::enable\u if\u t
    std::is\u same
    我创建了一个名为
    OptionHasValue
    的类型。如果模板参数
    Invert
    为false且
    T
    void
    OptionHasValue
    具有无效类型,否则它具有模板参数
    U
    指定的类型
    OptionValue
    使用
    OptionHasValue
    和一点SFINAE魔法来决定它是否应该具有支持值存储所需的方法。也就是说,
    OptionValue
    的第一个版本将
    OptionHasValue
    作为其第二个模板参数,因此如果
    T
    void
    ,则它将无效(并被编译器删除)。另一个版本的
    OptionValue
    具有相反的行为,因为它的第二个模板参数是
    OptionHasValue
    ,而
    true
    反转了
    OptionHasValue
    的行为
    选项
    本身继承自
    选项值
    ,因此如果您创建一个像
    选项
    这样的选项,它就不支持值(也就是说,它缺少像
    setValue
    setValueFromString
    getValue
    这样的函数)。另一方面,如果您创建一个类似于
    option
    的选项,则生成的类实例具有所有这些特性。
    现在的问题是,(例如)
    OptionSet::process()
    同时访问
    Option::hasValue
    Option::setValueFromString
    ,但后者仅在
    Option::hasValue
    为true时声明(并且该选项对应的模板参数不是
    void
    )。但是因为这里的
    Option::setValueFromString
    没有包装在某种模板中,编译器也会抱怨
  • 在我的
    main
    -函数中,我使用函数
    optionSet.getOptionValue(std::string)
    。此函数应返回选项的值(在调用
    process()
    后设置)。现在困难的是返回类型取决于
    findOptionByIdentifier
    的返回值,该函数循环遍历所有可用选项并返回带有所需标识符的选项。
    例如,如果
    identifier
    将是
    “number”
    (如本问题开头的
    选项的示例),则
    findOptionByIdentifier
    的返回类型将是
    选项
    ,因为唯一具有标识符
    的选项是“number”
    是将
    int
    作为其第一个模板参数的模板,它最终将导致
    getOptionValue
    具有返回类型
    int

    您可以在
    main
    -函数的最后几行的注释中看到预期的行为
  • 那么,为了修复所有这些问题(并使其可编译),我必须在下面的代码中更改什么?我使用的是g++5.2.0(mingw-w64),因此我可以使用C++11和C++14的任何功能

    #include <iostream>
    #include <vector>
    #include <string>
    #include <algorithm>
    #include <stdexcept>
    #include <type_traits>
    #include <boost/lexical_cast.hpp>
    #include <boost/any.hpp>
    
    
    
    template<typename T, bool Invert = false, typename U = void>
    using OptionHasValue = std::enable_if_t<(!std::is_same<T, void>::value) ^ Invert, U>;       //only make this template substitution successful, if (when 'Invert' is false) T is not if type 'void'
    
    template<typename T, typename Enable = void>
    class OptionValue;
    
    template<typename T>
    class OptionValue<T, OptionHasValue<T>>             //using SFINAE ("substitution failure is not an error") here
    {
        protected:
            T                               value;
    
        public:
            void setValue(T newValue)
            {
                value = newValue;
            }
    
            void setValueFromString(std::string newValueStr)
            {
                setValue(boost::lexical_cast<T>(newValueStr));
            }
    
            T getValue()
            {
                return value;
            }
    
            bool hasValue()
            {
                return true;                            //if this class variant is taken by the compiler, the 'Option' that will inherit from it will have a value
            }
    };
    
    template<typename T>
    class OptionValue<T, OptionHasValue<T, true>>       //the opposite condition (the 'true' inverts it)
    {
        //option value is disabled, but to check if a value is available in the derived class, add a function for that (or should I not?)
        public:
            bool hasValue()
            {
                return false;
            }
    };
    
    
    
    template<typename T>
    class Option : public OptionValue<T>
    {
        private:
            std::string                     identifier;
            std::vector<std::string>        variants;
    
        public:
            Option(std::string newIdentifier, std::vector<std::string> newVariants)
            {
                identifier = newIdentifier;
                variants = newVariants;
            }
    
            bool hasVariant(std::string v)
            {
                return (std::find(variants.begin(), variants.end(), v) != variants.end());
            }
    
            std::string getIdentifier()
            {
                return identifier;
            }
    };
    
    
    
    class OptionSet
    {
        private:
            std::vector<boost::any>         options;                                //boost::any can't be the right way to do this, or is it?
            std::vector<std::string>        argvVec;
    
            template<typename T>
            Option<T>& findOptionByIdentifier(std::string identifier)
            {
                for(auto& o : options)
                    if(o.getIdentifier() == identifier)                             //of course this doesn't compile, because 'o' will always be of type 'boost::any', but what should I do instead?
                        return o;
                throw std::runtime_error("error: unable to find option by identifier \"" + identifier + "\"\n");
            }
    
            template<typename T>
            Option<T>& findOptionByVariant(std::string variant)
            {
                for(auto& o : options)
                    if(o.hasVariant(variant))                                       //probably almost the same compile error like in 'findOptionByIdentifier'
                        return o;
                throw std::runtime_error("error: unable to find option by variant \"" + variant + "\"\n");
            }
    
        public:
            template<typename t>
            void add(Option<T> opt)
            {
                options.push_back(opt);                                             //is this the right way to add instances of classes with different template parameters to a vector?
            }
    
            void setArgvVec(std::vector<std::string> newArgvVec)
            {
                argvVec = newArgvVec;
            }
    
            void process()
            {
                for(size_t i=0; i<argvVec.size(); i++)
                {
                    Option<T>& opt = findOptionByVariant(argvVec[i]);               //of course this doesn't compile either, but what should I do instead?
                    if(opt.hasValue())
                    {
                        if(i == argvVec.size()-1)
                            throw std::runtime_error("error: no value given for option \"" + argvVec[i] + "\"\n");
                        opt.setValueFromString(argvVec[i]);                         //boost::bad_lexical_cast should be caught here, but that's not important right now
                        i++;
                    }
                }
            }
    
            template<typename T>
            T getOptionValue(std::string identifier)
            {
                Option<T>& opt = findOptionByIdentifier(identifier);                //a bit like the call to 'findOptionByVariant' in 'process()'. also, this variable does not have to be a reference
                if(!opt.hasValue())
                    throw std::runtime_error("error: option with identifier \"" + identifier + "\" has no value\n");
                return opt.getValue();
            }
    };
    
    
    
    int main()
    {
        OptionSet optionSet;
    
        //it's not guaranteed that OptionSet::add will always receive a rvalue, I just do it here for shorter code/simplicity
        optionSet.add(Option<void>("help", { "-?", "--help" }));                    //if it's a void-option, the 'Option' does not have a value, if the template parameter is anything else, it has one (like below)
        optionSet.add(Option<std::string>("message", { "-m", "--message" }));
        optionSet.add(Option<int>("number", { "-n", "--number" }));
        optionSet.add(Option<double>("pi", { "-p", "--pi" }));
    
        optionSet.setArgvVec({ "--help", "-m", "hello", "--number", "100", "--pi", "3.14" });
        optionSet.process();
    
        std::string message = optionSet.getOptionValue("message");
        int number = optionSet.getOptionValue("number");
        double pi = optionSet.getOptionValue("pi");
    
        std::cout << "Message: " << message << "\n";        //should output 'hello'
        std::cout << "Number:  " << number << "\n";         //should output '100'
        std::cout << "Pi:      " << pi << "\n";             //should output something like '3.140000'
    
        return 0;
    }
    
    #包括
    #包括
    #包括
    #包括
    #包括
    #包括
    #包括
    #包括
    模板
    使用OptionHasValue=std::启用//只有在类型为“void”的情况下(当“Invert”为false时)T不是时,才能使此模板替换成功
    模板
    类别期权价值;
    模板
    在此处使用SFINAE(“替换失败不是错误”)初始化OptionValue//
    {
    受保护的:
    T值;
    公众:
    无效设置值(T新值)
    {
    值=新值;
    }
    void setValueFromString(std::string newValueStr)
    {
    setValue(boost::词法转换(newValueStr));
    }
    T getValue()
    {
    返回值;
    }
    布尔值()
    {
    return true;//如果编译器采用该类变量,则将从中继承的“Option”将有一个值
    }
    };
    模板
    class OptionValue//相反的条件(“true”将其反转)
    {
    //选项值已禁用,但要检查派生类中是否有可用的值,请为此添加一个函数(或者我是否应该?)
    公众:
    布尔值()
    {
    返回false;
    }
    };
    模板
    类选项:公共选项值
    {
    私人:
    字符串标识符;
    std::载体变异体;
    公众:
    选项(std::string newIdentifier,std::vector newVariants)
    {
    标识符=新标识符;
    变体=新变体;
    }
    布尔变量(标准::字符串v)
    {
    return(std::find(variants.begin(),variants.end(),v)!=variants.end());
    }
    std::string getIdentifier()
    {
    返回标识符;
    }
    };
    O类