简体   繁体   English

C ++模板调用成员函数,如果匹配类型,则抛出异常?

[英]C++ Template Call member function if matching type else throw exception?

Consider the following class: 考虑以下课程:

class Example {
    const void * pValue;
    (Example& (*SIndex(const void *,char *)));
    (Example& (*NIndex(const void *,unsigned long long *)));

    template<class T,typename I> static  Example & Index(const T* value,I index)
    { return Example(value->operator[](index)); }

public:
    template<class T> Example(const T& value) {
        pValue = &value;
        SIndex = (Example& (*(const void *,char *)))Index<T,char *>;
        NIndex = (Example& (*(const void *,unsigned long long)))Index<T,unsigned long long>;
    };
    Example& operator[](char * index) { return SIndex(pValue,index); };
    Example& operator[](unsigned long long) { return NIndex(pValue,index); };
}

I wish to use SFINAE (Substitution failure is not an error) (see http://en.wikipedia.org/wiki/Substitution_failure_is_not_an_error ), to alter _Index such that if there is no matching operator for the class it will throw an exception rather than return a new Example object. 我希望使用SFINAE(替换失败不是错误)(参见http://en.wikipedia.org/wiki/Substitution_failure_is_not_an_error ),改变_Index,这样如果该类没有匹配的运算符,它将抛出异常而不是而不是返回一个新的Example对象。 ie for _Index<T,char *> will match operator[](char *) or operator[](std::string&) as std::string can be constructed from char * while _Index<T,unsigned long long> will match any numerical type, as unsigned long long can be converted. 即对于_Index<T,char *>将匹配operator[](char *)operator[](std::string&)因为std::string可以从char *构造而_Index<T,unsigned long long>将匹配任何数字类型,因为unsigned long long可以转换。

I have only just come across SFINAE while looking for solutions to this problem it clear the solution would lay in this, however I'm not sure how to implement it. 我只是遇到了SFINAE,同时寻找这个问题的解决方案,很明显解决方案就在于此,但我不确定如何实现它。

Example (As Requested): This will compile, currently the output is the pointer value no content, but that is fixable, Also I disabled the String Index Option as that causes a compiler error with out working SFINAE. 示例(如请求):这将编译,当前输出是指针值没有内容,但这是可修复的,我也禁用了字符串索引选项,因为这导致编译器错误而不工作SFINAE。 Note it real world application obj1 and obj2 may be different types ie vector and map however will produce errors until SFINAE is added to the code to produce exceptions instead. 请注意,现实世界的应用程序obj1和obj2可能是不同的类型,即矢量和地图但是会产生错误,直到SFINAE被添加到代码中以产生异常。 Also the "index path" to open will be detriment at run time NOT compile time. 此外,打开的“索引路径”在运行时不会编译时会有害。 Note: I'm working on extending this code https://github.com/vitaut/format to allow ids of the type {0[4][first]} or {1[error]} etc. Since not all object support these methods the object not supporting it should produce an runtime exception when the format string asks for it, also must not produce compiler errors, as any type used as an argument will be passed in to the Arg class, Example, just shows the part of the More complex Arg class I need to modify to get this working. 注意:我正在努力扩展此代码https://github.com/vitaut/format以允许{0 [4] [first]}或{1 [error]}等类型的ID。因为并非所有对象支持这些方法不支持它的对象应该在格式字符串请求时产生运行时异常,也不得产生编译错误,因为任何用作参数的类型都会传递给Arg类,例如,只显示部分我需要修改更复杂的Arg类才能使其工作。

#include <iostream>
#include <string>
#include <vector>
#include <map>

using namespace std;

class Example {
    void * pValue;
    /*Example& (*SIndex)(void*,const char *);*/
    Example& (*NIndex)(void*,unsigned int);
    template<class T,typename I> static Example& Index(T* obj,I index) {
        return *(new Example((*obj)[index])); }
public:
    Example(char value) { pValue = &value; }
    template<class T> Example(T& value) {
        pValue = &value;
        /*SIndex = (Example& (*)(void*,const char *))Index<T,const char *>;*/
        NIndex = (Example& (*)(void*,unsigned int))Index<T,unsigned int>;
    }
    /*Example& operator[](const char * index) { return SIndex(pValue,index); }*/
    Example& operator[](unsigned int index) { return NIndex(pValue,index); }
    void * Get() { return pValue; }
};

int main()
{
    vector<Example> Objs;
    vector<string> obj1;
    vector<string> obj2;
    obj1.push_back("Hello ");
    obj1.push_back("World");
    obj2.push_back("Olleh ");
    obj2.push_back("Dlorw");
    Objs.push_back(Example(obj1));
    Objs.push_back(Example(obj2));
    cout << (string *)(Objs[0][0].Get()) << endl;
    cout << (string *)(Objs[1][1].Get()) << endl;
    cin.ignore();
    return 0;
}

Example 2 (with Simple's SFINALE code) v2: 示例2(使用Simple的SFINALE代码)v2:

#include <iostream>
#include <string>
#include <vector>
#include <map>
#include <utility>
#include <type_traits>

using namespace std;

template<typename T, typename I>
decltype(void((*declval<T>())[declval<I>()]), true_type())
has_subscript_test(int);

template<typename, typename>
false_type
has_subscript_test(...);

template<typename T, typename I>
struct has_subscript : decltype(has_subscript_test<T, I>(0))
{
};

class Example {
    void * pValue;
    Example& (*SIndex)(void*,const char *);
    Example& (*NIndex)(void*,unsigned int);
    template<class T,typename I> static
    typename enable_if<has_subscript<T, I>::value, Example&>::type
    Index(T* obj,I index) { return *(new Example((*obj)[index])); }
    template<class T,typename I> static
    typename enable_if<!has_subscript<T, I>::value, Example&>::type
    Index(T* obj,I index) { throw "Invalid Index Type"; }
public:
    //Example(char value) { pValue = &value; }
    template<class T> Example(T& value) {
        pValue = &value;
        SIndex = (Example& (*)(void*,const char *))Index<T,const char *>;
        NIndex = (Example& (*)(void*,unsigned int))Index<T,unsigned int>;
    }
    Example& operator[](const char* index) { return SIndex(pVale,index); }
    Example& operator[](unsigned int index) { return NIndex(pValue,index); }
    void * Get() { return pValue; }
};

int main()
{
    vector<Example> Objs;
    vector<string> obj1;
    map<string,string> obj2;
    obj1.push_back("Hello ");
    obj1.push_back("World");
    obj2["A"] = "Olleh ";
    obj2["B"] = "Dlorw";
    Objs.push_back(Example(obj1));
    Objs.push_back(Example(obj2));
    cout << (string *)(Objs[0][(unsigned int)0].Get()) << endl;
    cout << (string *)(Objs[1][(const char *)"B"].Get()) << endl;
    cin.ignore();
    return 0;
}

compiler errors: Looks like only the first 2 are important the rest are a side effect of those 2. 编译器错误:看起来只有前2个是重要的,其余的是那些2的副作用。

g++.exe -Wall -fexceptions  -std=c++11 -g     -c C:/SourceCode/Test/main.cpp -o ../obj/Debug/main.o
cygwin warning:
  MS-DOS style path detected: C:/SourceCode/Test/main.cpp
  Preferred POSIX equivalent is: /cygdrive/c/SourceCode/Test/main.cpp
  CYGWIN environment variable option "nodosfilewarning" turns off this warning.
  Consult the user's guide for more details about POSIX paths:
    http://cygwin.com/cygwin-ug-net/using.html#using-pathnames
C:/SourceCode/Test/main.cpp: In instantiation of ‘static typename std::enable_if<has_subscript<T, I>::value, Example&>::type Example::Index(T*, I) [with T = std::vector<std::basic_string<char> >; I = const char*; typename std::enable_if<has_subscript<T, I>::value, Example&>::type = Example&]’:
C:/SourceCode/Test/main.cpp:37:9:   required from ‘Example::Example(T&) [with T = std::vector<std::basic_string<char> >]’
C:/SourceCode/Test/main.cpp:54:32:   required from here
C:/SourceCode/Test/main.cpp:29:64: error: invalid conversion from ‘const char*’ to ‘std::vector<std::basic_string<char> >::size_type {aka unsigned int}’ [-fpermissive]
In file included from /usr/lib/gcc/i686-pc-cygwin/4.7.3/include/c++/vector:65:0,
                 from C:/SourceCode/Test/main.cpp:3:
/usr/lib/gcc/i686-pc-cygwin/4.7.3/include/c++/bits/stl_vector.h:750:7: error:   initializing argument 1 of ‘std::vector<_Tp, _Alloc>::reference std::vector<_Tp, _Alloc>::operator[](std::vector<_Tp, _Alloc>::size_type) [with _Tp = std::basic_string<char>; _Alloc = std::allocator<std::basic_string<char> >; std::vector<_Tp, _Alloc>::reference = std::basic_string<char>&; std::vector<_Tp, _Alloc>::size_type = unsigned int]’ [-fpermissive]
C:/SourceCode/Test/main.cpp: In instantiation of ‘static typename std::enable_if<has_subscript<T, I>::value, Example&>::type Example::Index(T*, I) [with T = std::map<std::basic_string<char>, std::basic_string<char> >; I = unsigned int; typename std::enable_if<has_subscript<T, I>::value, Example&>::type = Example&]’:
C:/SourceCode/Test/main.cpp:38:9:   required from ‘Example::Example(T&) [with T = std::map<std::basic_string<char>, std::basic_string<char> >]’
C:/SourceCode/Test/main.cpp:55:32:   required from here
C:/SourceCode/Test/main.cpp:29:64: error: invalid user-defined conversion from ‘unsigned int’ to ‘std::map<std::basic_string<char>, std::basic_string<char> >::key_type&& {aka std::basic_string<char>&&}’ [-fpermissive]
In file included from /usr/lib/gcc/i686-pc-cygwin/4.7.3/include/c++/string:54:0,
                 from /usr/lib/gcc/i686-pc-cygwin/4.7.3/include/c++/bits/locale_classes.h:42,
                 from /usr/lib/gcc/i686-pc-cygwin/4.7.3/include/c++/bits/ios_base.h:43,
                 from /usr/lib/gcc/i686-pc-cygwin/4.7.3/include/c++/ios:43,
                 from /usr/lib/gcc/i686-pc-cygwin/4.7.3/include/c++/ostream:40,
                 from /usr/lib/gcc/i686-pc-cygwin/4.7.3/include/c++/iostream:40,
                 from C:/SourceCode/Test/main.cpp:1:
/usr/lib/gcc/i686-pc-cygwin/4.7.3/include/c++/bits/basic_string.h:487:7: note: candidate is: std::basic_string<_CharT, _Traits, _Alloc>::basic_string(const _CharT*, const _Alloc&) [with _CharT = char; _Traits = std::char_traits<char>; _Alloc = std::allocator<char>] <near match>
/usr/lib/gcc/i686-pc-cygwin/4.7.3/include/c++/bits/basic_string.h:487:7: note:   no known conversion for argument 1 from ‘unsigned int’ to ‘const char*’
C:/SourceCode/Test/main.cpp:29:64: error: invalid conversion from ‘unsigned int’ to ‘const char*’ [-fpermissive]
In file included from /usr/lib/gcc/i686-pc-cygwin/4.7.3/include/c++/string:54:0,
                 from /usr/lib/gcc/i686-pc-cygwin/4.7.3/include/c++/bits/locale_classes.h:42,
                 from /usr/lib/gcc/i686-pc-cygwin/4.7.3/include/c++/bits/ios_base.h:43,
                 from /usr/lib/gcc/i686-pc-cygwin/4.7.3/include/c++/ios:43,
                 from /usr/lib/gcc/i686-pc-cygwin/4.7.3/include/c++/ostream:40,
                 from /usr/lib/gcc/i686-pc-cygwin/4.7.3/include/c++/iostream:40,
                 from C:/SourceCode/Test/main.cpp:1:
/usr/lib/gcc/i686-pc-cygwin/4.7.3/include/c++/bits/basic_string.h:487:7: error:   initializing argument 1 of ‘std::basic_string<_CharT, _Traits, _Alloc>::basic_string(const _CharT*, const _Alloc&) [with _CharT = char; _Traits = std::char_traits<char>; _Alloc = std::allocator<char>]’ [-fpermissive]
C:/SourceCode/Test/main.cpp:29:64: error: conversion to non-const reference type ‘std::map<std::basic_string<char>, std::basic_string<char> >::key_type&& {aka class std::basic_string<char>&&}’ from rvalue of type ‘std::basic_string<char>’ [-fpermissive]

This should work: 这应该工作:

template<typename T, typename I>
decltype(void((*std::declval<T>())[std::declval<I>()]), std::true_type())
has_subscript_test(int);

template<typename, typename>
std::false_type
has_subscript_test(...);

template<typename T, typename I>
struct has_subscript : decltype(has_subscript_test<T, I>(0))
{
};

class Example {
    const void * pValue;
    (Example& (*SIndex(const void *,char *)));
    (Example& (*NIndex(const void *,unsigned long long *)));

    template<class T, typename I> static
    typename std::enable_if<has_subscript<T, I>::value, Example&>::type
    _Index(const T* value,I index) { return Example((*value)[index]); }

    template<class T, typename I> static
    typename std::enable_if<!has_subscript<T, I>::value, Example&>::type
    _Index(const T*,I) { throw "uh-oh!"; }
public:
    template<class T> Example(const T& value) {
        pValue = &value;
        SIndex = (Example& (*(const void *,char *)))_Index<T,char *>;
        NIndex = (Example& (*(const void *,unsigned long long)))_Index<T,unsigned long long>;
    };
    Example& operator[](char * index) { return SIndex(this,index); };
    Example& operator[](unsigned long long) { return NIndex(this,index); };
};

I've done the SFINAE bit but there are other problems with your code. 我已完成SFINAE位,但您的代码还有其他问题。 _Index is not allowed as a name, you're returning a temporary by reference and you're trying to cast a member pointer to a function pointer (even with a different function type!) _Index不允许作为名称,你通过引用返回一个临时的,并且你正在尝试将一个成员指针_Index为函数指针(即使使用不同的函数类型!)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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